Files are available by executing ~classque/usp-conditionvariables on the CS network.
This material is taken from the Section 13.4 of USP
Problem: How do you have a thread wait for a condition without busy waiting?
If the condition is simple, such as waiting for an integer to be greater than 0,
we can do this with semaphores.
Example: wait(empty)
When another thread creates a buffer slot, it can execute: signal(empty) If we have more general condition, such as x!=y we can also do it with
semaphore, but not in a simple way.
We cannot solve this problem with mutexes alone.
To do this with mutexes we would have to:
lock the shared variables (with a mutex)
check the condition
If the condition is not satisfied, block the thread
But we must first unlock the shared variables or no other
thread can access them to change them.
If the change occurs between the unlocking of the shared variables
and the blocking of the thread, we might miss the change.
We need to atomically unlock the mutex and block the process.
This is what condition variables do.
About the name condition variable:
A condition variable is associated with a mutex, not with a condition.
They get their name from the way they are used (or the problem they are used to solve),
not from what they are.
There are 2 basic operations you can do with condition variables: wait and signal
Question: In how many different contexts have we seen the names wait and signal
wait:
associate the condition variable with a mutex
atomically unlock the mutex and block the thread
blocking the thread means: remove from the running state and put in a queue
of threads waiting for a signal operation on the condition variable.
A condition variable is an object that has a mutex and a list of threads waiting for
something to happen.
Compare this to a mutex or a semaphore.
Compare this to a monitor condition variable.
signal:
wake up a thread, if any, that is waiting for the condition variable, and have it attempt
to acquire (lock) the mutex.
You perform this signal operation with the mutex already locked, since presumably you
just modified the shared variable.
Immediately after you do the signal on the condition variable, you should
unlock the mutex (so the process you woke up can eventually enter the run state).
POSIX condition variable syntax:
Creating and initializing a condition variable:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
int pthread_cond_signal(pthread_cond_t *cond);
Rules
pthread_cond_wait may only be called by a thread that owns the mutex.
When it returns, it will own the mutex again.
The waiting thread should check the condition again (in a loop) to see if it is satisfied.
Typically a thread calls signal when it changes a variable, not necessarily when the
condition is satisfied.
In fact, even if the condition was satisfied at the time of wake up, it might not
be satisfied when the awakened thread executes.
pthread_cond_signal should be called while the thread owns the mutex,
but this is not required.
Threads that modify or test the shared variables involved in the condition should
own the mutex.
This includes the threads that call wait and signal.
Examples: (no error checking)
Example 13.13: wait for the condition x==y
pthread_mutex_lock(&m);
while (x != y)
pthread_cond_wait(&v, &m);
/* modify x or y if necessary */
pthread_mutex_unlock(&m);
Example 13.14: modify a shared variable and wake up a thread to check the condition:
Example 13.22: wait for test_condition() to be true:
static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t v = PTHREAD_COND_INITIALIZER;
pthread_mutex_lock(&m);
while (!test_condition()) /* get resource */
pthread_cond_wait(&v, &m);
/* do critical section, possibly changing test_condition() */
pthread_cond_signal(&v); /* inform another thread */
pthread_mutex_unlock(&m);
A complete example: a barrier
A barrier is a synchronization construct that prevents all threads
from continuing until they all have reached a certain place in their execution.
When it does happen, all threads can proceed.
See Program 13.13, tbarrier.c on page 472
It uses pthread_cond_broadcast that wakes up all threads waiting for the
condition variable. This is similar to notifyAll in Java monitors.
See Section 13.4 of USP for more information about POSIX condition variables.
Next Notes