eCos Reference Manual
Prev |  | Next

# Condition Variables

## Name

cyg_cond_init, cyg_cond_destroy, cyg_cond_wait, cyg_cond_timed_wait, cyg_cond_signal, cyg_cond_broadcast -- Synchronization primitive

## Synopsis

    #include <cyg/kernel/kapi.h>

``void cyg_cond_init`(cyg_cond_t* cond, cyg_mutex_t* mutex);`

``void cyg_cond_destroy`(cyg_cond_t* cond);`

``cyg_bool_t cyg_cond_wait`(cyg_cond_t* cond);`

``cyg_bool_t cyg_cond_timed_wait`(cyg_cond_t* cond, cyg_tick_count_t abstime);`

``void cyg_cond_signal`(cyg_cond_t* cond);`

``void cyg_cond_broadcast`(cyg_cond_t* cond);`

## Description

Condition variables are used in conjunction with mutexes to implement long-term waits for some condition to become true. For example consider a set of functions that control access to a pool of resources:

    cyg_mutex_t res_lock;
    res_t res_pool[RES_MAX];
    int res_count = RES_MAX;

    void res_init(void)
    {
        cyg_mutex_init(&res_lock);
        <fill pool with resources>
    }

    res_t res_allocate(void)
    {
        res_t res;

        cyg_mutex_lock(&res_lock);               // lock the mutex

        if( res_count == 0 )                     // check for free resource
            res = RES_NONE;                      // return RES_NONE if none
        else
        {
            res_count--;                         // allocate a resources
            res = res_pool[res_count];
        }

        cyg_mutex_unlock(&res_lock);             // unlock the mutex

        return res;
    }

    void res_free(res_t res)
    {
        cyg_mutex_lock(&res_lock);               // lock the mutex

        res_pool[res_count] = res;               // free the resource
        res_count++;

        cyg_mutex_unlock(&res_lock);             // unlock the mutex
    }

These routines use the variable `res_count` to keep track of the resources available. If there are none then `res_allocate` returns `RES_NONE`, which the caller must check for and take appropriate error handling actions.

Now suppose that we do not want to return `RES_NONE` when there are no resources, but want to wait for one to become available. This is where a condition variable can be used:

    cyg_mutex_t res_lock;
    cyg_cond_t res_wait;
    res_t res_pool[RES_MAX];
    int res_count = RES_MAX;

    void res_init(void)
    {
        cyg_mutex_init(&res_lock);
        cyg_cond_init(&res_wait, &res_lock);
        <fill pool with resources>
    }

    res_t res_allocate(void)
    {
        res_t res;

        cyg_mutex_lock(&res_lock);               // lock the mutex

        while( res_count == 0 )                  // wait for a resources
            cyg_cond_wait(&res_wait);

        res_count--;                             // allocate a resource
        res = res_pool[res_count];

        cyg_mutex_unlock(&res_lock);             // unlock the mutex

        return res;
    }

    void res_free(res_t res)
    {
        cyg_mutex_lock(&res_lock);               // lock the mutex

        res_pool[res_count] = res;               // free the resource
        res_count++;

        cyg_cond_signal(&res_wait);              // wake up any waiting allocators

        cyg_mutex_unlock(&res_lock);             // unlock the mutex
    }

In this version of the code, when `res_allocate` detects that there are no resources it calls `cyg_cond_wait`. This does two things: it unlocks the mutex, and puts the calling thread to sleep on the condition variable. When `res_free` is eventually called, it puts a resource back into the pool and calls `cyg_cond_signal` to wake up any thread waiting on the condition variable. When the waiting thread eventually gets to run again, it will re-lock the mutex before returning from `cyg_cond_wait`.

There are two important things to note about the way in which this code works. The first is that the mutex unlock and wait in `cyg_cond_wait` are atomic: no other thread can run between the unlock and the wait. If this were not the case then a call to `res_free` by that thread would release the resource but the call to `cyg_cond_signal` would be lost, and the first thread would end up waiting when there were resources available.

The second feature is that the call to `cyg_cond_wait` is in a `while` loop and not a simple `if` statement. This is because of the need to re-lock the mutex in `cyg_cond_wait` when the signalled thread reawakens. If there are other threads already queued to claim the lock then this thread must wait. Depending on the scheduler and the queue order, many other threads may have entered the critical section before this one gets to run. So the condition that it was waiting for may have been rendered false. Using a loop around all condition variable wait operations is the only way to guarantee that the condition being waited for is still true after waiting.

Before a condition variable can be used it must be initialized with a call to `cyg_cond_init`. This requires two arguments, memory for the data structure and a pointer to an existing mutex. This mutex will not be initialized by `cyg_cond_init`, instead a separate call to `cyg_mutex_init` is required. If a condition variable is no longer required and there are no threads waiting on it then `cyg_cond_destroy` can be used.

When a thread needs to wait for a condition to be satisfied it can call `cyg_cond_wait`. The thread must have already locked the mutex that was specified in the `cyg_cond_init` call. This mutex will be unlocked and the current thread will be suspended in an atomic operation. When some other thread performs a signal or broadcast operation the current thread will be woken up and automatically reclaim ownership of the mutex again, allowing it to examine global state and determine whether or not the condition is now satisfied. The kernel supplies a variant of this function, `cyg_cond_timed_wait`, which can be used to wait on the condition variable or until some number of clock ticks have occurred. The mutex will always be reclaimed before `cyg_cond_timed_wait` returns, regardless of whether it was a result of a signal operation or a timeout.

There is no `cyg_cond_trywait` function because this would not serve any purpose. If a thread has locked the mutex and determined that the condition is satisfied, it can just release the mutex and return. There is no need to perform any operation on the condition variable.

When a thread changes shared state that may affect some other thread blocked on a condition variable, it should call either `cyg_cond_signal` or `cyg_cond_broadcast`. These calls do not require ownership of the mutex, but usually the mutex will have been claimed before updating the shared state. A signal operation only wakes up the first thread that is waiting on the condition variable, while a broadcast wakes up all the threads. If there are no threads waiting on the condition variable at the time, then the signal or broadcast will have no effect: past signals are not counted up or remembered in any way. Typically a signal should be used when all threads will check the same condition and at most one thread can continue running. A broadcast should be used if threads check slightly different conditions, or if the change to the global state might allow multiple threads to proceed.

## Valid contexts

`cyg_cond_init` is typically called during system initialization but may also be called in thread context. The same applies to `cyg_cond_delete`. `cyg_cond_wait` and `cyg_cond_timedwait` may only be called from thread context since they may block. `cyg_cond_signal` and `cyg_cond_broadcast` may be called from thread or DSR context.

Prev | Home | Next
Mutexes | Up | Semaphores