Condition variable is used for a thread to wait for some condition to be true before continuing. The implementation is quite simple compared to lock, yet the difficult part is to understand how a CV is supposed to used.
CV Interface
Condition variable has two interfaces: cv_wait
and cv_signal
. cv_wait
is
used to wait for a condition to be true, and cv_signal
is used to notify other
threads that a certain condition is true.
So what?
Let's consider a producer-consumer case, where a bunch of threads share a resource pool, some of them (producer) is responsible to put stuff to the pool and others (consumer) are responsible to take stuff from the pool. Obviously, we have two rules.
- If the pool is full, then producers can not put to the pool
- If the pool is empty, then consumers can not take stuff from the pool
And we use two condition variables for each of these rules: pool_full
and
pool_empty
. Here is the pseudo code for producer and consumer:
void producer(void) {
lock_acquire(pool_lock);
while (pool_is_full) {
cv_wait(pool_full, pool_lock);
}
produce();
/* notify that the pool now is not empty, so if any one is waiting
* on the pool_empty cv, wake them up
*/
cv_signal(pool_empty, pool_lock);
lock_release(pool_lock);
}
void consumer(void) {
lock_acquire(pool_lock);
while (pool_is_empty) {
cv_wait(pool_empty, pool_lock);
}
consume();
/* notify that the pool now is not full, so if any one is waiting
* on the pool_full cv, wake them up
*/
cv_signal(pool_full, pool_lock);
lock_release(pool_lock);
}
Here we also use a lock to protect access to the pool. We can see from this example:
- Condition variable is virtually a wait channel
- Condition variable is normally used together with lock, but condition variable itself doesn't contain a lock
What's in cv
structure?
Obviously, we need a wait channel. And that's it (probably plus a cv_name
).
cv_wait
and cv_signal
Now let's get to business. The comment in $OS161_SRC/kern/inlucde/synch.h
basically told you everything you need to do.
In cv_wait
, we need to:
- Lock the wait channel
- Release the lock passed in
- Sleep on the wait channel
- When waked up, re-acquire the lock.
So before cv_wait
, we should already hold the lock (so that we can release
it). And after cv_wait
, we still hold the lock.
In cv_signal
, we just wake up somebody in the wait channel using
wchan_wakeone
.