Comtrol eCos User Manual
Page 73

Condition Variables
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
73