I was reading this
nice blog from A Williams:
http://www.justsoftwaresolutions.co.uk/threading/condition-variable-spurious-wakes.html
and one thing bugs me:
when thread wakes up because of the spurious wake does it have mutex locked?
boost::mutex::scoped_lock lock(the_mutex);
while(the_queue.empty())
{
the_condition_variable.wait(lock);
}
I guess so because otherwise call to .empty would be unsafe but Im not sure.
Yes, it does have the mutex locked. Basically, the mutex gets released only while the thread is blocked in the_condition_variable.wait(). Spurious wakeup or not, the mutex is locked everywhere else in the code that you show.
From the documentation for boost::condition_variable::wait():
Postcondition:
lock is locked by the current thread.
Related
Consider this:
// Somewhere
std::mutex mutex;
std::unique_lock lock{mutex};
std::condition_variable condition;
// Thread01
condition.wait(lock);
// Thread02
while (lock.owns_lock());
So I got a situation like this where the loop at Thread02 never ends, regardless of the fact that Thread01 is waiting for the condition.
This means that unlocking the lock at std::condition_variable::wait does not synchronize with checking if the lock is locked at std::unique_lock::owns_lock. Here it is explicitly told that the wait "Atomically unlocks lock..." but here nothing is told about owns_lock of being atomic or being synchronised with the lock operations.
So the question is: how can I atomically check it the wait has atomically unlocked the lock?
EDIT:
The desire is to know at Thread02 if Thread01 is waiting for the condition. That's why I've accepted the answer.
You can momentarily lock the lock and get the desired Thread02:
// Thread02
std::unique_lock{mutex};
// mutex isn’t locked
I have a std::condition_variable_any that waits on a custom lock which is a composition of two mutexes (one std::mutex and one shared-locked std::shared_mutex). Its unlock() operation simply unlocks both mutexes sequentially.
For example (pseudocode):
mutex mutex1;
shared_mutex mutex2;
condition_variable_any cv;
// acquiring the locks
DualLock lock(unique_lock(mutex1), shared_lock(mutex2));
// waiting
cv.wait(lock);
cv.wait() should atomically unlock both mutex1 and mutex2, and put the thread to sleep until cv gets notified.
It it still guaranteed that the thread is sleeping and listening to the condition variable notification, once any of mutex1 or mutex2 is unlocked?
Or is it possible that one mutex gets unlocked, and a second thread locks it, sends the notification, but this first thread was not yet sleeping and listening. So the notification never arrived and no wakeup occurs.
If DualLock meets the requirements of BasicLockable, then the code will perform as hoped. If not, it won't.
BasicLockable reference is here
So the notification never arrived and no wakeup occurs.
This can never happen when condition variables are used properly. The wakeup of a condition variable cannot be interpreted as a signal of an event, since the documentation of condition_variable explains that there can be spurious wakeups.
At best, the notification is an indication that now may be a good time to test the condition you're waiting for (and you may do so in the secure knowledge that the test is protected by the mutex).
The state of the condition and the state of the blocked-ness of the current thread are two separate concerns.
Here's a better example (assumes that DualLock models BasicLockable correctly):
bool condition_met = false;
mutex mutex1;
shared_mutex mutex2;
condition_variable_any cv;
// acquiring the locks
DualLock lock(unique_lock(mutex1), shared_lock(mutex2));
while(!condition_met)
{
// waiting
cv.wait(lock);
}
// whatever happens, you have the lock here
// ...work
lock.unlock();
Your notifier would notify this way:
DualLock lock(unique_lock(mutex1), shared_lock(mutex2));
condition_met = true;
lock.unlock(); // note the order: unlock mutex first...
cv.notify_all(); // ...then notify the condition variable
In a multithreaded environment, we have the following two functions:
std::mutex mtx;
std::condition_variable cv;
void waiter()
{
std::unique_lock<std::mutex> lck(mtx);
//...
cv.wait(lck);
//...
}
void notifier()
{
std::unique_lock<std::mutex> lck(mtx);
//...
cv.notify_one();
}
Assume that the waiter executes first and then waits on the condition_variable. Then the notifier executes and notifies the waiter. The waiter tries to reacquire the mutex after the notifier has released it.
Question: Is it possible that some other thread locks the mutex right after the notifier has released it but still before the waiter gets it? If yes, what has to be done so that this cannot happen? And if yes, I also don't understand the purpose of condition_variable. The purpose should be to block a thread until some condition is fulfilled. But if, after the condition is fulfilled and the thread has been woken up, there is the chance that again the condition is not fulfilled, what's the point?
Is it possible that some other thread locks the mutex right after the notifier has released it but still before the waiter gets it?
Yes.
If yes, what has to be done so that this cannot happen?
Nothing.
And if yes, I also don't understand the purpose of condition_variable. The purpose should be to block a thread until some condition is fulfilled. But if, after the condition is fulfilled and the thread has been woken up, there is the chance that again the condition is not fulfilled, what's the point?
It's even more complicated. The thread can be woken up if the condition variable was not notified at all. This is called spurious wakeup (doc).
The point of condition variable is to block a thread until some other thread notifies it. In order to address the "condition is not fulfilled when the waiting thread gets a chance to execute" issue usually the waiting thread waits in a loop until the condition is fulfilled. The standard library even has a shortcut for that, there is a void wait( std::unique_lock<std::mutex>& lock, Predicate pred ); overload that does exactly that. See the doc.
I am working with condition variables and I am assuming they unlock their associated mutex on wait. Otherwise, the mutex would never be released. Yet, I can't find this information on any documentation. Consider the following code:
std::condition_variable consumerWakeMeUp;
std::mutex queueMutex;
// this locks the mutex
std::unique_lock<std::mutex> lk(queueMutex);
// going to sleep now
consumerWakeMeUp.wait(lk);
Does the "consumerWakeMeUp.wait(lk)" unlock the mutex? It must I assume otherwise the thread would hand on that mutext forever. But if anyone knows more the details I'd appreciate the input.
thank you.
never mind
"Atomically releases lock, blocks the current executing thread, and adds it to the list of threads waiting on *this. The thread will be unblocked when notify_all() or notify_one() is executed. It may also be unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait exits. If this function exits via exception, lock is also reacquired. (until C++14)"
I am implementing manual reset event using pthread in Linux which is similar to WaitForSingleEvent in Windows. I found this post
pthread-like windows manual-reset event
and follow it, however there a thing that confuse me:
void mrevent_wait(struct mrevent *ev) {
pthread_mutex_lock(&ev->mutex);
while (!ev->triggered)
pthread_cond_wait(&ev->cond, &ev->mutex);
pthread_mutex_unlock(&ev->mutex);
}
pthread_cond_wait:
Atomically release mutex and cause the calling thread to block on the condition variable cond;
pthread_mutex_unlock:
Attempts to unlock the specified mutex. If the mutex type is PTHREAD_MUTEX_NORMAL, error detection is not provided. If a thread attempts to unlock a mutex that is has not locked or a mutex which is unlocked, undefined behavior results.
What I am scare is when pthread_cond_wait release the mutex, then pthread_mutex_unlock may come undefined behavior (this kind of thing would drive me crazy, how come they not handle it :-D )
Thank you.
The standard says:
Upon successful return, the mutex has
been locked and is owned by the
calling thread.
Which means that when returning, pthread_cond_wait atomically locks the associated mutex.
The workflow is like this:
You lock a mutex
pthread_cond_wait atomically blocks and unlocks the mutex (so other threads might get here)
When a condition arrives, pthread_cond_wait atomically returns and locks the mutex
You unlock the mutex
I don't think pthread_cond_wait blocks
and unlocks
That's because you didn't read the link I provided.
These functions atomically release
mutex and cause the calling thread to
block on the condition variable cond;