6

As you know, condition variables should be called in cycle to avoid spurious wake-ups. Like this:

while (not condition)
    condvar.wait();

If another thread wants to wake up waiting thread, it must set condition flag to true. E.g.:

condition = true;
condvar.notify_one();

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

1)Waiting thread checks condition flag, and finds it is equal to FALSE, so, it's going to enter condvar.wait() routine.

2)But just before this (but after condition flag checking) waiting thread is preempted by kernel (e.g. because of time slot expiration).

3) At this time, another thread wants to notify waiting thread about condition. It sets condition flag to TRUE and calls condvar.notify_one();

4) When kernel scheduler runs first thread again, it enters condvar.wait() routine, but the notification have been already missed.

So, waiting thread can't exit from condvar.wait(), despite condition flag is set to TRUE, because there is no wake up notifications anymore.

Is it possible?

HttpWatchSupport
  • 2,804
  • 1
  • 17
  • 16
Vasily
  • 235
  • 3
  • 14
  • possible duplicate of [Why do pthreads’ condition variable functions require a mutex?](http://stackoverflow.com/questions/2763714/why-do-pthreads-condition-variable-functions-require-a-mutex) – Stephan Dollberg Feb 25 '13 at 17:28
  • 1
    See also, [Calling pthread_cond_signal without locking mutex](http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex?rq=1) – Hasturkun Feb 25 '13 at 17:40
  • @bamboon. Yes, these questions are quite similar. I've just approached this problem by another side. – Vasily Feb 26 '13 at 11:14

4 Answers4

16

That is exactly why a condition variable must be used in conjunction with a mutex, in order to atomically update the state and signal the change. The full code would look more like:

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

and for the other thread:

lock_guard<mutex> lock(mutex);
condition = true;
condvar.notify_one();
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • [link](http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all) - don't see where notifying thread locks mutex. Does this page contains error? – Vasily Feb 25 '13 at 17:35
  • @Vasily That example shows a design flaw, which it circumvents by just sleeping for a second. It could suffer from the problem you are describing. There could be situations where that just wouldn't matter. – Stephan Dollberg Feb 25 '13 at 17:36
  • Yes, I see now. If notifying thread has to have access to this mutex, it's an explanation. Thanks. I thought it doesn't. – Vasily Feb 25 '13 at 17:39
  • 7
    I would also mention the fact that class `condition_variable` in C++11 has an overload of the `wait()` member function which accepts a predicate as its second argument, and that internally realizes the loop. – Andy Prowl Feb 25 '13 at 18:01
  • 5
    Why once `std::unique_lock` and once `std::lock_guard`? – TheOperator Apr 14 '16 at 13:26
4

You example missing small part, but that explains why that 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 condition to true under the same mutex lock, this situation will not happen.

Slava
  • 43,454
  • 1
  • 47
  • 90
  • I doubt it. Notifying thread knows nothing about this mutex, so, it can't be used for access restriction. If I'd call condvar.notify_one(mutex), it would've been correct. But I don't use any mutexes in notifying thread – Vasily Feb 25 '13 at 17:24
  • @Vasily The point is that the other thread can't notify the waiting thread because it is currently blocked on the mutex. – Stephan Dollberg Feb 25 '13 at 17:25
  • Notifying thread changing variable that is protected by this mutex lock, so it has to be aware of the mutex – Slava Feb 25 '13 at 17:25
  • @bamboon Why can't it? I don't see, how mutex prevents to send notification – Vasily Feb 25 '13 at 17:29
  • @Slava Look. Until you call condvar.wait(mutex), condvar doesn't know about mutex existance. So, before waiting thread enters this routine, no way notifying thread can know about this mutex, even if it has access to condvar. – Vasily Feb 25 '13 at 17:31
  • @Vasily The notifier thread can't propagate and send the notification because it doesn't hold the mutex and it can't acquire it because the waiting thread holds it and that one will only release it once it is in `wait`. – Stephan Dollberg Feb 25 '13 at 17:34
  • 1
    @Vasily: Whoever signals the condvar should be doing it under the same mutex. This eliminates the wake/wait race completely. If your mutex isn't known to the notifying thread, what is it protecting, exactly? – Hasturkun Feb 25 '13 at 17:34
  • Yes, I understand now. It looks like notifying thread has to lock the same mutex before notify_one call. Manual I've read missed this part. – Vasily Feb 25 '13 at 17:44
  • 3
    @Vasily I am not sure if notify_one has to be called under mutex, but changing condition variable (not condvar) definately requires mutex, and that does not have to be in documentation. Another problem that some people are changing bool or int on intel platform without proper lock, assuming it is safe, but that not quite correct. – Slava Feb 25 '13 at 17:59
  • @Slava Right, it looks like I don't have to lock notify_one. Only condition. – Vasily Feb 25 '13 at 18:13
2

Mike Seymour his answer is incomplete because there is a race condition which ends up with wakeup lost. The right way is to (now with the c++11) is as follow:

Thread1:

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

Thread2:

{
    std::lock_guard<std::mutex> lck(myMutex);
    condition = true;
} // unlock here! prevent wakeup lost
condvar.notify_one();
Maor Dahan
  • 356
  • 4
  • 8
-2

Yes (I tested this in December 2012), and there is a solution I conjured up for this a while ago. The "Flare" class: Note that it uses a spin lock, but the time spent in this is minimal.

Declaration (hpp):

class Flare
{
public:
/**
\brief Flare's constructor.
\param fall_through_first, will skip the first wait() if true.
*/
Flare(bool fall_through_first = false);


/**
\brief Flare's 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;

};

Implementaton/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();
}
Ultimate Hawk
  • 580
  • 4
  • 16
  • So, you've been able to reproduce this 'spurious blocking'? How? – Vasily Feb 25 '13 at 17:33
  • 1
    You only need one mutex, not two. Proper use of a mutex and condition variable (ie. set condition and signal under the same mutex) ensures these races don't occur. – Hasturkun Feb 25 '13 at 17:38
  • It's reproducible by having two threads continously notify eachother's cnd_vars with an atomic boolean to avoid spurious wakeups. They'll lock eachother up. Don't forget that any calls to notify_one() when the other thread has not yet entered wait() is a notification that is lost. Hasturkin mind giving us an example? Whenever the notifier in my class has finished, it may allow the waiter to iterate many times without halt. – Ultimate Hawk Feb 25 '13 at 17:52
  • It looks like you can avoid the problem just by locking the same mutex from notifying thread. Encapsulation all this actions in one class is possible, but now always desirable. – Vasily Feb 25 '13 at 18:01
  • 2
    @BourgondAries: Waiter does this `lock(mtx); while(!cond) wait(cndvar, mtx); ...; unlock(mtx);` Notifier does `lock(mtx); cond = true; signal(cndvar); unlock(mtx);`. If notifier is called before waiter, waiter never sleeps (since cond is true). No deadlocks occur (as waiting on the condvar also releases the mutex). waiter always wakes up, ordering doesn't matter. You also don't need to use an atomic boolean (since you already have a mutex). – Hasturkun Feb 26 '13 at 12:21