Condition Variables
Condition variables are variables that represent certain conditions and can
only be used in monitors. Associated with each condition variable, there
is a queue of threads and two operations: condition signal and
condition wait. When a thread calls condition wait, the caller is
put into the
queue of that condition variable. When a thread calls condition signal,
if there are threads waiting in that condition variable's queue, one of them
is released. Otherwise, the condition signal is
lost.
There is a subtle problem, however. If condition variables can only be used
in a monitor, a condition signal can only be performed within a
monitor. If there are waiting threads, one of them will be released and
as a result there are two threads executing within
the monitor. One of them is the caller that triggers the release
and the other is the one being released. While there are some details and
different ways to prevent this from happening, for our purpose, we can assume
only one of these two threads will be executing. It could be the caller or
the one being released.
In our simulation example, a mutex lock is used to protect the monitor from
having more than one threads in execution. This lock mechanism causes a
little complication. More precisely, if a thread calls condition signal to
release another thread and the released thread receives the control, then the
monitor lock owned by the caller must be released and be given to the released
thread. How could this exchange of lock be implemented?
Fortunately, all problems can be solved easily. Let us start with the
declaration of a condition variable.
Declaration and Initialization
To declare a condition variable, use type cond_t. To initialize it,
use function cond_init(). Note that all condition variable related
names start with cond_.
#include <synch.h>
cond_t CondVar;
int cond_init(
cond_t *CondVar, /* ptr to cond. var. */
USYNC_THREAD, /* use this in CS270 */
(void *) NULL); /* always use this */
- Function cond_init() takes three arguments. The second and
the third are always USYNC_THREAD and (void *) NULL,
respectively, in this course. The first argument is a pointer to
the condition variable to be initialized.
- Since condition variables can only be used within monitors,
it is a good practice to have the initialization calls in the
monitor initialization function.
- Multiple threads must not initialize the same condition variable
at the same time. Therefore, it would be better to initialize
condition variables before any thread starts.
Blocks on a Condition Variable
A thread can call function cond_wait() to block itself until the event
represented by that condition variable occurs.
#include <synch.h>
cond_t CondVar;
int cond_wait(
cond_t *CondVar, /* ptr to cond. var. */
mutex_t *Mutex); /* assoc. mutex ptr */
- Function cond_wait() has two arguments. The first one is the
condition variable on which the caller blocks. The second one is
a mutex lock, which is usually the lock of the monitor
that contains this condition variable.
This lock must be owned by the calling
thread.
- If this call is successful, the calling threads blocks on the
indicated condition variable and the lock owned by the calling
thread is released. In other words, after the calling thread
blocks, the lock of the monitor is ``open'' and hence other threads
can acquire it and execute within the monitor.
- To wake up a thread blocked by a condition variable, use function
cond_signal(). The awakened thread
will automatically be regranted the lock it owned prior to
the wait.
- However, the condition that the awakened
thread has been waiting may be changed before the thread starts
execution. Thus, re-evaluation the condition is required.
The following is a typical use of a conditional variable within a
monitor. MonitorLock is a mutex lock for locking the monitor and
CondVar is a condition variable.
#include <synch.h>
mutex_t MonitorLock;
cond_t CondVar;
mutex_lock(&MonitorLock);
while (!cond)
cond_wait(&CondVar, &MonitorLock);
mutex_unlock(&MonitorLock);
- Function calls mutex_lock(&MonitorLock) and
mutex_unlock(&MonitorLock) simulate entering and exiting
the monitor.
- Variable cond is used to indicate if the condition is
fulfilled or not. Before calling cond_wait(), a thread
should test to see if the condition is met (i.e., this is
what the while is for). If the condition is met, there
is no need to wait. Note that there is no race condition here
when testing the value of cond, because the monitor is
already locked. If the condition is not met, then the thread must
wait by calling cond_wait().
- The call to function cond_wait() will put the caller thread
into the queue of the condition variable given as the first
argument and, at the same time, the lock owned by the calling
thread (i.e., the second argument) is taken way so that
other thread can lock the monitor.
- When some other thread signals this condition variable, one thread
will be released. At this moment, the awakened thread will get
the lock back and should retest the condition since the
condition may be changed in the period the thread is awakened
and the time the thread actually starts its execution.
Signals a Condition Variable
To signal a condition variable, use cond_signal():
#include <synch.h>
cond_t CondVar;
int cond_signal(
cond_t *CondVar); /* ptr to cond. var. */
- If there is at least one threads were blocked on the condition
variable, one of them is unblocked. The awakened thread will be
automatically granted the lock it originally owned when executed
cond_wait().
- When no threads are blocked on the condition variable,
cond_signal() has no effect.
- Calling cond_signal() should also be protected with a
lock that is used to protect the call to cond_wait().
The following is a typical use of cond_wait() and
cond_signal():
#include <synch.h>
mutex_t MonitorLock;
cond_t CondVar;
mutex_lock(&MonitorLock);
while (!cond)
cond_wait(&CondVar, &MonitorLock);
mutex_unlock(&MonitorLock);
mutex_lock(&MonitorLock);
if (cond)
cond_signal(&CondVar);
mutex_unlock(&MonitorLock);
We have discussed the cond_wait() part. Since threads are
continually testing a variable to know if a condition is met, some thread
who takes the responsibility to signal a condition variable must set the
condition to indicate that the condition occurs. In the above, if condition
cond is met, a thread calls cond_signal(). Thus, one thread
waiting on that condition is awakened, retests the condition, and
perhaps continues.