Condition variables are a standard form of thread synchronization. They are designed to be used with mutexes. The associated mutex is used to ensure that a condition can be checked atomically, and that the thread can block on the associated condition variable without missing either a change to the condition or a signal that the condition has changed.
Table 3–4 lists the condvar(9F) interfaces.
Table 3–4 Condition Variable Routines
Name |
Description |
---|---|
cv_init(9F) |
Initializes a condition variable |
cv_destroy(9F) |
Destroys a condition variable |
cv_wait(9F) |
Waits for condition |
cv_timedwait(9F) |
Waits for condition or timeout |
cv_wait_sig |
Waits for condition or return zero on receipt of a signal |
cv_timedwait_sig(9F) |
Waits for condition or timeout or signal |
cv_signal(9F) |
Signals one thread waiting on the condition variable |
cv_broadcast(9F) |
Signals all threads waiting on the condition variable |
Declare a condition variable (type kcondvar_t) for each condition. Usually, this is done in the driver's soft-state structure. Use cv_init(9F) to initialize each one. Similar to mutexes, condition variables are usually initialized at attach(9E) time. For example:
cv_init(&xsp->cv, NULL, CV_DRIVER, NULL);
For a more complete example of condition variable initialization see Chapter 5, Driver Autoconfiguration.
To use condition variables, follow these steps in the code path waiting for the condition:
Acquire the mutex guarding the condition.
Test the condition.
If the test results do not allow the thread to continue, use cv_wait(9F) to block the current thread on the condition. cv_wait(9F) releases the mutex before blocking. Upon return from cv_wait(9F) (which will reacquire the mutex before returning), repeat the test.
Once the test allows the thread to continue, set the condition to its new value. For example, set a device flag to busy.
Release the mutex.
Follow these steps in the code path signaling the condition:
Acquire the mutex guarding the condition.
Set the condition.
Release the mutex.
Example 3–1 uses a busy flag along with mutex and condition variables to force the read(9E) routine to wait until the device is no longer busy before starting a transfer.
static int xxread(dev_t dev, struct uio *uiop, cred_t *credp) { struct xxstate *xsp; ... mutex_enter(&xsp->mu); while (xsp->busy) cv_wait(&xsp->cv, &xsp->mu); xsp->busy = 1; mutex_exit(&xsp->mu); /* perform the data access */ } static uint_t xxintr(caddr_t arg) { struct xxstate *xsp = (struct xxstate *)arg; mutex_enter(&xsp->mu); xsp->busy = 0; cv_broadcast(&xsp->cv); mutex_exit(&xsp->mu); }