4

Is there a canonical pattern for a thread to check if it should stop working?

The scenario is that a thread is spinning a tight working loop but it should stop if another thread tells it to. I was thinking of checking an atomic bool in the loop condition but I'm not sure if that is an unnecessary performance hit or not. E.g.

std::atomic<bool> stop{false};
while(!stop){
  //...
}
Enlico
  • 23,259
  • 6
  • 48
  • 102
Daniel
  • 379
  • 4
  • 15
  • Time it. Is it having a significant effect? – Martin James Nov 19 '20 at 14:09
  • There are possible alternatives, but without details of the loop....... – Martin James Nov 19 '20 at 14:10
  • The use for my app is to have a dedicated thread reading sockets, but now I'm thinking that's a bad idea anyway. But the question is general, I suppose there's no better way than atomic bool in general, considering no-one has answered. – Daniel Nov 20 '20 at 06:03
  • why would it be a,bad idea? What is a,bad idea is an atomic boolean since, reading from a socket, the thread would block anyway and so not read the bool. Reading from a socket is not a tight loop! – Martin James Nov 20 '20 at 07:48
  • ....anyway, if it is reading socket data, why stop the thread anyway? Set your atomic boolean and have the thread, if the read returns, just discard incoming socket data instead of processing it in its 'usual' manner, or disconnect the socket, or whatever.. – Martin James Nov 20 '20 at 07:53
  • You are absolutely right that the atomic bool perf is not a worry if you are blocking anyway. The socket thread I describe is actually reading from multiple sockets and then sleeping with kqueue but I'm not sure if that is the best approach either. – Daniel Nov 20 '20 at 11:07

2 Answers2

2

You have to make it atomic (or volatile in old C/C++) to ensure that the compiler doesn't optimize it away and only test stop once.

If you call a function in the loop that cannot be inlined (like reading sockets) you might be safe with a non-atomic bool, but why risk it - especially as the atomic read is unlikely to be a performance issue in that case?

To have the least effect you could do something like:

std::atomic<bool> stop;
void rx_thread() {
  // ...
  while(!stop.load(std::memory_order_relaxed)){
    ..
  }
}
Hans Olsson
  • 11,123
  • 15
  • 38
-1

I don't see any reason why the bool should be atomic. There isn't really a potential for a race condition. When you want to stop the thread, you first set the variable to true and then issue a call that wakes up the blocking function inside the loop (however you do that, depends on the blocking call).

bool stop = false;

void rx_thread() {
  // ...
  while(!stop){
    // ...
    blocking_call();
    // ...
  }
}

void stop_thread() {
  stop = true;
  wakeup_rx_thread();
}

If the blocking call happens to wake up between setting stop to true and calling wakeup_rx_thread(), then the loop will finish anyway. The call to wakeup_rx_thread() will be needless, but that doesn't matter.

Georg P.
  • 2,785
  • 2
  • 27
  • 53
  • 1
    I may be wrong but I was under the impression that bools are not atomic on all possible architectures per the standard, so there is potential for tearing (although unlikely). – Daniel Nov 20 '20 at 10:59
  • You're right, it isn't atomic! But why would it need to be atomic here? There is no place in the code above, where you would need to read and write it in an atomic manner. – Georg P. Nov 20 '20 at 11:08
  • I see, yes your design is quite nice. This could work. Thank you. – Daniel Nov 20 '20 at 11:10
  • 2
    *I don't see any reason why the bool should be atomic* -1. A simple `bool stop` is not guaranteed to work without the guarantees provided by `atomic`. First - look at your currently-posted code - the compiler sees `bool stop = false;` **and never sees anything that changes `stop`**, so it's free to elide the check in `while (!stop)` and **replace it with an infinite loop**. Second, [`volatile` is insufficient for use in multithreaded code](https://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading). So yes, `atomic` or similar is necessary. – Andrew Henle Nov 20 '20 at 12:01
  • Oh bloody compilers... :-[ – Georg P. Nov 20 '20 at 12:19
  • 1
    Note that compilers are not only free to do this, but some do it for the code above - IF stop and stop_thread are static. If they are non-static the compiler cannot easily do it since blocking_call could theoretically change stop or call stop_thread. – Hans Olsson Nov 20 '20 at 13:10
  • 1
    How did people do it before C++11? Or in C? Use an OS-specific mutex? – Georg P. Nov 20 '20 at 13:49
  • @GeorgP. They often used volatile; since it generally worked in practice - regardless of whether that use of volatile - and even multi-threading in general - was standardized by the language. But now we have better possibilities. – Hans Olsson Nov 23 '20 at 08:15