Please see the following code:
std::mutex mutex;
std::condition_variable cv;
std::atomic<bool> terminate;
// Worker thread routine
void work() {
while( !terminate ) {
{
std::unique_lock<std::mutex> lg{ mutex };
cv.wait(lg);
// Do something
}
// Do something
}
}
// This function is called from the main thread
void terminate_worker() {
terminate = true;
cv.notify_all();
worker_thread.join();
}
Is the following scenario can happen?
- Worker thread is waiting for signals.
- The main thread called
terminate_worker()
;- The main thread set the atomic variable
terminate
totrue
, and then signaled to the worker thread. - Worker thread now wakes up, do its job and load from
terminate
. At this step, the change toterminate
made by the main thread is not yet seen, so the worker thread decides to wait for another signal.
- The main thread set the atomic variable
- Now deadlock occurs...
I wonder this is ever possible. As I understood, std::atomic
only guarantees no race condition, but memory order is a different thing. Questions:
- Is this possible?
- If this is not possible, is this possible if
terminate
is not an atomic variable but is simplybool
? Or atomicity has nothing to do with this? - If this is possible, what should I do?
Thank you.
I don't believe, what you describe is possible, as
cv.notify_all()
afaik (please correct me if I'm wrong) synchronizes withwait()
, so when the worker thread awakes, it will see the change toterminate
.However:
A deadlock can happen the following way:
Worker thread (WT) determines that the
terminate
flag is still false.The main thread (MT) sets the
terminate
flag and callscv.notify_all()
.join
and blocks.cv.wait()
) and blocks too.Solution:
While you don't have to hold a lock while you call cv.notify, you
terminate
(even if it is an atomic)wait
happen while you are holding the same lock.This is why there is a form of
wait
that performs this check just before it sends the thread to sleep.A corrected code (with minimal changes) could look like this: