As it is said that Mutex are needed to protect the Condition Variables.
Is the reference here to the actual condition variable declared as pthread_cond_t
OR
A normal shared variable count
whose values decide the signaling and wait.
?
As it is said that Mutex are needed to protect the Condition Variables.
Is the reference here to the actual condition variable declared as pthread_cond_t
OR
A normal shared variable count
whose values decide the signaling and wait.
?
A condition variable must always be associated with a mutex, to avoid the race condition where a thread prepares to wait on a condition variable and another thread signals the condition just before the first thread actually waits on it.
More info here
Some Sample:
Thread 1 (Waits for the condition)
Thread 2 (Signals the condition)
As you can see in the same above, the mutex protects the variable 'i' which is the cause of the condition. When we see that the condition is not met, we go into a condition wait, which implicitly releases the mutex and thereby allowing the thread doing the signalling to acquire the mutex and work on 'i' and avoid race condition.
Now, as per your question, if the signalling thread signals first, it should have acquired the mutex before doing so, else the first thread might simply check the condition and see that it is not being met and might go for condition wait and since the second thread has already signalled it, no one will signal it there after and the first thread will keep waiting forever.So, in this sense, the mutex is for both the condition & the conditional variable.
Per the pthreads docs the reason that the mutex was not separated is that there is a significant performance improvement by combining them and they expect that because of common race conditions if you don't use a mutex, it's almost always going to be done anyway.
https://linux.die.net/man/3/pthread_cond_wait
I thought that a better use-case might help better explain conditional variables and their associated mutex.
I use posix conditional variables to implement what is called a Barrier Sync. Basically, I use it in an app where I have 15 (data plane) threads that all do the same thing, and I want them all to wait until all data planes have completed their initialization. Once they have all finished their (internal) data plane initialization, then they can start processing data.
Here is the code. Notice I copied the algorithm from Boost since I couldnt use templates in this particular application:
Notice that every thread that enters the
barrierSync()
method locks the mutex, which makes everything between the mutex lock and the call to eitherpthread_cond_wait()
orpthread_mutex_unlock()
atomic. Also notice that the mutex is released/unlocked inpthread_cond_wait()
as mentioned here. In this link it also mentions that the behavior is undefined if you callpthread_cond_wait()
without having first locked the mutex.If
pthread_cond_wait()
did not release the mutex lock, then all threads would block on the call topthread_mutex_lock()
at the beginning of thebarrierSync()
method, and it wouldnt be possible to decrease thebarrierCounter_
variables (nor manipulate related vars) atomically (nor in a thread safe manner) to know how many threads have calledbarrierSync()
So to summarize all of this, the mutex associated with the Conditional Variable is not used to protect the Conditional Variable itself, but rather it is used to make the logic associated with the condition (
barrierCounter_
, etc) atomic and thread-safe. When the threads block waiting for the condition to become true, they are actually blocking on the Conditional Variable, not on the associated mutex. And a call topthread_cond_broadcast/signal()
will unblock them.Here is another resource related to
pthread_cond_broadcast()
andpthread_cond_signal()
for an additional reference.The reference is to both.
The mutex makes it so that the shared variable (
count
in your question) can be checked and if the value of that variable doesn't meet the desired condition the wait that is performed insidepthread_cond_wait()
will occur atomically with respect to that check.The problem being solved with the mutex is that you have two separate operations that need to be atomic:
count
pthread_cond_wait()
if the condition isn't met yet.A
pthread_cond_signal()
doesn't 'persist' - if there are no threads waiting on thepthread_cond_t
object, a signal does nothing. So if there wasn't a mutex making the two operations listed above atomic with respect to one another you could find yourself in the following situation:count
is non-zeroThread B will signal when it increments
count
(which will setcount
to something other than zero)count
and finds that it's zeropthread_cond_wait()
, thread "B" comes along and incrementscount
to 1 and callspthread_cond_signal()
. That call actually does nothing of consequence since "A" isn't waiting on thepthread_cond_t
object yet.pthread_cond_wait()
, but since condition variable signals aren't remembered, it will block at this point and wait for the signal that has already come and gone.The mutex (as long as all threads are following the rules) makes it so that item #2 cannot occur between items 1 and 3. The only way that thread "B" will get a chance to increment
count
is either before A looks atcount
or after "A" is already waiting for the signal.