I've looked into the VC++ implementation of std::condition_variable(lock,pred)
, basically, it looks like this:
template<class _Predicate>
void wait(unique_lock<mutex>& _Lck, _Predicate _Pred)
{ // wait for signal and test predicate
while (!_Pred())
wait(_Lck);
}
Basically , the naked wait
calls _Cnd_waitX
which calls _Cnd_wait
which calls do_wait
which calls cond->_get_cv()->wait(cs);
(all of these are in the file cond.c).
cond->_get_cv()
returns Concurrency::details::stl_condition_variable_interface
.
If we go to the file primitives.h
, we see that under windows 7 and above, we have the class stl_condition_variable_win7
which contains the old good win32 CONDITION_VARIABLE
, and wait
calls __crtSleepConditionVariableSRW
.
Doing a bit of assembly debug, __crtSleepConditionVariableSRW
just extract the the SleepConditionVariableSRW
function pointer, and calls it.
Here's the thing: as far as I know, the win32 CONDITION_VARIABLE
is not a kernel object, but a user mode one. Therefore, if some thread notifies this variable and no thread actually sleep on it, you lost the notification, and the thread will remain sleeping until timeout has reached or some other thread notifies it. A small program can actually prove it - if you miss the point of notification - your thread will remain sleeping although some other thread notified it.
My question goes like this:
one thread waits on a condition variable and the predicate returns false. Then, the whole chain of calls explained above takes place. In that time, another thread changed the environment so the predicate will return true and notifies the condition variable. We passed the predicate in the original thread, but we still didn't get into SleepConditionVariableSRW
- the call chain is very long.
So, although we notified the condition variable and the predicate put on the condition variable will definitely return true (because the notifier made so), we are still blocking on the condition variable, possibly forever.
Is this how should it behave? It seems like a big ugly race condition waiting to happen. If you notify a condition variable and it's predicate returns true - the thread should unblock. But if we're in the limbo between checking the predicate and going to sleep - we are blocked forever. std::condition_variable::wait
is not an atomic function.
What does the standard says about it and is it a really race condition?