I m using pthread_cond_wait(&cond_t, &mutex);
in my program and I m wondering why this function need as a second parameter a mutex variable.
Does the pthread_cond_wait()
unlock the mutex at the beggining (beggining of the execution pthread_cond_wait()
) and then locked when it finish (just before leaving pthread_cond_wait()
)?
There are many text on the subject of condition variables and their usage, so I'll not bore you with a ton of ugly details. The reason they exist at all is to allow you to notify change in a predicate state. The following are critical in understanding proper use of condition variables and their mutex association:
pthread_cond_wait()
simultaneously unlocks the mutex and begins waiting for the condition variable to be signalled. thus you must always have ownership of the mutex before invoking it.pthread_cond_wait()
returns with the mutex locked, thus you must unlock the mutex to allow its use somewhere else when finished with it. Whether the return happened because the condition variable was signalled or not isn't relevant. You still need to check your predicate regardless to account for potential spurious wakeups.The purpose of the mutex is not to protect the condition variable; it is to protect the predicate on which the condition variable is being used as a signaling mechanism. This is hands-down the most often misunderstood idiom of pthread condition variables and their mutexes. The condition variable doesn't need mutual exclusion protection; the predicate data does. Think of the predicate as an outside-state which is being monitored by the users of the condition-variable/mutex pair.
For example, a trivial yet obviously wrong piece of code to wait for a boolean flag
fSet
:I should be obvious the main problem is the predicate,
fSet
, is not protected at all. Many things can go wrong here. Ex: From the time you evaluate the while-conditon until the time you begin waiting (or spinning, or whatever) the value may have changed. If that change notification is somehow missed, you're needlessly waiting.We can change this a little so at least the predicate is protected somehow. Mutual exclusion in both modifying and evaluating the predicate is easily provided with (what else) a mutex.
Well, that seems simple enough.. Now we never evaluate the predicate without first getting exclusive access to it (by latching the mutex). But this is still a major problem. We latched the mutex, but we never release it until our loop is finished. If everyone else plays by the rules and waits for the mutex lock before evaluation or modification of
fSet
, they're never be able to do so until we give up the mutex. The only "someone" that can do that in this case is us.So what about adding still more layers to this. Will this work?
Well, yes it will "work", but still is not much better. The period between
XXXXX
andYYYYY
we don't own the mutex (which is ok, since we're not checking or modifyingfSet
anyway). But anytime during that period some other thread can (a) obtain the mutex, (b) modifyfSet
, and (c) release the mutex, and we won't know a thing about it until we finish oursleep()
, once-again obtain the mutex lock, and loop around for another check.There has to be a better way. Somehow there should be a way that we can release the mutex and begin waiting for some sort of signal that tells us a change in the predicate may have happened. Equally important, when we receive that signal and return to our code, we should already own the lock that grants us access to check the predicate data. This is exactly what a condition-variable is designed to provide.
The Condition Variable In Action
Enter the condition-variable + mutex pair. The mutex protects access to changing or checking the predicate, while the condition variable sets up a system of monitoring a change, and more importantly, doing so atomically (as far as you're concerned, anyway) with the predicate mutual exclusion:
For some other thread to signal the loop above, there are several ways to do it, the two most popular below:
Another way...
Each has different intrinsic behavior and I invite you to do some homework on those differences and determine which is more appropriate for specific circumstances. The former provides better program flow at the expense of introducing potentially unwarranted wake-ups. The latter reduces those wake-ups but at the price of less context synergy. Either will work in our sample, and you can experiment with how each affects your waiting loops. Regardless, one thing paramount, and both methods fulfill this mandate:
Never change, nor check, the predicate condition unless the mutex is locked. Ever.
Simple Monitor Thread
This type of operation is common in a monitor thread that acts on a specific predicate condition, which (sans' error checking) typically looks something like this:
A More Complex Monitor Thread
Modifying this basic form to account for a notification system that doesn't require you to keep the mutex latched once you've picked up the notification becomes a little more involved, but not by very much. Below is a monitor proc that does not keep the mutex latched during regular processing once we've established we've been served (so to speak).
Where would someone use something like that ? Well, suppose your "predicate" is the "state" of a work queue as well as some flag to tell you to stop looping and exit. Upon receiving the notification that something is "different", you check to see if you should continue executing your loop, and deciding you should continue, pop some data off the queue. Modifying the queue requires the mutex be latched (remember, its "state" is part of our predicate). Once we have popped our data, we have it locally and can process it independent of the queue state, so we release the mutex, do our thing, then require the mutex for the next go-around. There are many ways to code the above concept, including judicious use of
pthread_cond_broadcast
, etc. But the basic form is hopefully understandable.This turned out to be considerably longer than I had hoped, but this is a major hurdle for people learning pthread-programming, and I feel it is worth the extra time/effort. I hope you got something out of it.
When the first thread calls
pthread_cond_wait(&cond_t, &mutex);
it releases the mutex and it waits till conditioncond_t
is signaled as complete andmutex
is available.So when
pthread_cond_signal
is called in the other thread, it doesn't "wake up" the thread that waits yet.mutex
must be unlocked first, only then there is a chance that first thread will get a lock, which means that "upon successful return ofpthread_cond_wait
mutex shall have been locked and shall be owned by the calling thread."yes it unlocks, waits for the condition to be fulfilled and then waits till it can reaquire the passed mutex.