4

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?

  1. Worker thread is waiting for signals.
  2. The main thread called terminate_worker();
    • The main thread set the atomic variable terminate to true, and then signaled to the worker thread.
    • Worker thread now wakes up, do its job and load from terminate. At this step, the change to terminate made by the main thread is not yet seen, so the worker thread decides to wait for another signal.
  3. 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:

  1. Is this possible?
  2. If this is not possible, is this possible if terminate is not an atomic variable but is simply bool? Or atomicity has nothing to do with this?
  3. If this is possible, what should I do?

Thank you.

8
  • std::memory_order Commented Dec 27, 2016 at 8:06
  • @melak47 Please provide more detail. I know this is about std::memory_order. But I have no idea how std::condition_variable acts with memory order restrictions. Commented Dec 27, 2016 at 8:19
  • Related: stackoverflow.com/questions/8819095/… Commented Dec 27, 2016 at 8:35
  • @n.m. which is already done. OP asks about ordering/synchronization between atomic store (with memor_order_seq_cst) and condition_variable::notify_all. Commented Dec 27, 2016 at 8:56
  • 1
    This code works as intended whenterminate is not atomic. Making it atomic is redundant and wasteful. Commented Dec 27, 2016 at 17:14

1 Answer 1

2

I don't believe, what you describe is possible, as cv.notify_all() afaik (please correct me if I'm wrong) synchronizes with wait(), so when the worker thread awakes, it will see the change to terminate.

However:

A deadlock can happen the following way:

  1. Worker thread (WT) determines that the terminate flag is still false.

  2. The main thread (MT) sets the terminate flag and calls cv.notify_all().

  3. As no one is curently waiting for the condition variable that notification gets "lost/ignored".
  4. MT calls join and blocks.
  5. WT goes to sleep ( cv.wait()) and blocks too.

Solution:

While you don't have to hold a lock while you call cv.notify, you

  • have to hold a lock, while you are modifying terminate (even if it is an atomic)
  • have to make sure, that the check for the condition and the actual call to 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:

// Worker thread routine void work() { while( !terminate ) { { std::unique_lock<std::mutex> lg{ mutex }; if (!terminate) { cv.wait(lg); } // Do something } // Do something } } // This function is called from the main thread void terminate_worker() { { std::lock_guard<std::mutex> lg(mutex); terminate = true; } cv.notify_all(); worker_thread.join(); } 
Sign up to request clarification or add additional context in comments.

3 Comments

In any case, OP should check after wait what's the state of terminate variable. In provided code we cannot distinguash why condition variable was notified. it can be either terminete request or normal synchronization with another thread.
@paweldac: Yes, he probably should (without knowing the business logic it is hard to tell), but what does that have to do with the question whether a deadlock can occure or not?
Thank you very much. I found that in the link [en.cppreference.com/w/cpp/thread/condition_variable] it is described that "Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread." Is it about what you just described? Or is a different story? And also, is it meaningless to make terminate atomic? that is, I can use just bool, isn't it?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.