Std :: condition_variable false lock

As you know, variable conditions must be called in a loop to avoid false awakenings. Like this:

while (not condition) condvar.wait(); 

If another thread wants to wake up the waiting thread, it must set the condition flag to true. For instance:.

 condition = true; condvar.notify_one(); 

I wonder if it is possible for the condition variable to be blocked by this scenario:

1) The waiting thread checks the status flag and finds that it is FALSE, so it will introduce the condvar.wait() procedure.

2) But before that (but after checking the status flag) the waiting thread is unloaded by the kernel (for example, due to the expiration of the time interval).

3) At this time, another thread wants to notify the pending thread of the status. It sets the condition flag to TRUE and calls condvar.notify_one();

4) When the kernel manager starts the first thread again, it enters the condvar.wait() procedure, but the notification has already been skipped.

Thus, the waiting thread cannot exit condvar.wait() , even though the status flag is set to TRUE because there are no more wake-up notifications.

Is it possible?

+6
source share
4 answers

That is why a condition variable must be used in conjunction with a mutex to atomize a state update and signal a change. The full code will be more like:

 unique_lock<mutex> lock(mutex); while (not condition) condvar.wait(lock); 

and for another thread:

 lock_guard<mutex> lock(mutex); condition = true; condvar.notify_one(); 
+15
source

For example, a small part is missing, but this explains why this is not possible if done correctly:

 while (not condition) // when you check condition mutex is locked condvar.wait( mutex ); // when you wait mutex is unlocked 

So, if you change the condition to true with the same mutex lock, this situation will not happen.

+4
source

Mike Seymour, his answer is incomplete because there is a race condition that ends in a lost awakening. The correct way (now with C ++ 11) is this:

Thread1:

 std::unique_lock<std::mutex> lck(myMutex); condvar.wait(lck, []{ return condition; }); // prevent spurious wakeup // Process data 

Thread 2:

 { std::lock_guard<std::mutex> lck(myMutex); condition = true; } // unlock here! prevent wakeup lost condvar.notify_one(); 
0
source

Yes (I tested this in December 2012), and there is a solution that I created for this some time ago. Class "Flare": Note that it uses a spin lock, but the time spent on it is minimal.

Ad (hpp):

 class Flare { public: /** \brief Flare constructor. \param fall_through_first, will skip the first wait() if true. */ Flare(bool fall_through_first = false); /** \brief Flare destructor. Takes care of removing the object of this class. */ ~Flare(); /** \brief Notifies the same object of availability. Any thread waiting on this object will be freed, and if the thread was not waiting, it will skip wait when it iterates over it. */ void notify(); /** \brief Wait until the next notification. If a notification was sent whilst not being inside wait, then wait will simply be skipped. */ void wait(); private: std::mutex m_mx; // Used in the unique_lock, std::unique_lock<std::mutex> m_lk; // Used in the cnd_var std::condition_variable m_cndvar; std::mutex m_in_function, n_mx; // protection of re-iteration. bool m_notifications; }; 

Implementation / Definition (cpp):

 #include "Flare.hpp" // PUBLIC: Flare::Flare(bool fall_through_first) : m_lk(m_mx), m_notifications(!fall_through_first) {} Flare::~Flare() {} void Flare::notify() { if (m_in_function.try_lock() == true) { m_notifications = false; m_in_function.unlock(); } else // Function is waiting. { n_mx.lock(); do { m_notifications = false; m_cndvar.notify_one(); } while (m_in_function.try_lock() == false); n_mx.unlock(); m_in_function.unlock(); } } void Flare::wait() { m_in_function.lock(); while (m_notifications) m_cndvar.wait(m_lk); m_in_function.unlock(); n_mx.lock(); m_notifications = true; n_mx.unlock(); } 
-2
source

All Articles