Writing Device Drivers for Oracle® Solaris 11.2

Exit Print View

Updated: September 2014
 
 

Condition Variables in Thread Synchronization

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.

The condvar(9F) functions are:

cv_broadcast(9F)

Signals all threads waiting on the condition variable.

cv_destroy(9F)

Destroys a condition variable.

cv_init(9F)

Initializes a condition variable.

cv_signal(9F)

Signals one thread waiting on the condition variable.

cv_timedwait(9F)

Waits for condition, time-out, or signal. See Threads Unable to Receive Signals.

cv_timedwait_sig(9F)

Waits for condition or time-out.

cv_wait(9F)

Waits for condition.

cv_wait_sig(9F)

Waits for condition or return zero on receipt of a signal. See Threads Unable to Receive Signals.

Initializing Condition Variables

Declare a condition variable of type kcondvar_t for each condition. Usually, the condition variables are declared in the driver's soft-state structure. Use cv_init(9F) to initialize each condition variable. Similar to mutexes, condition variables are usually initialized at attach(9E) time. A typical example of initializing a condition variable is:

cv_init(&xsp->cv, NULL, CV_DRIVER, NULL);

For a more complete example of condition variable initialization, see Chapter 6, Driver Autoconfiguration.

Waiting for the Condition

To use condition variables, follow these steps in the code path waiting for the condition:

  1. Acquire the mutex guarding the condition.

  2. Test the condition.

  3. If the test results do not allow the thread to continue, use cv_wait(9F) to block the current thread on the condition. The cv_wait(9F) function releases the mutex before blocking the thread and reacquires the mutex before returning. On return from cv_wait(9F), repeat the test.

  4. After the test allows the thread to continue, set the condition to its new value. For example, set a device flag to busy.

  5. Release the mutex.

Signaling the Condition

Follow these steps in the code path to signal the condition:

  1. Acquire the mutex guarding the condition.

  2. Set the condition.

  3. Signal the blocked thread with cv_broadcast(9F).

  4. Release the mutex.

The following example 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.

Example 3-1  Using Mutexes and Condition Variables
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);
}