liveBook · Manning

Condition variable

As explained in Modern C, Third Edition

A condition variable is a thread-synchronization object that allows threads to wait until a shared-state condition may have become true.

Wait for accounting work

The accounting thread holds L->mtx while it tests shared state: mtx_lock(&L->mtx); while (!L->finished && L->accounted == L->iteration) life_wait(&L->acco, &L->mtx); L->acco is a condition variable of type cnd_t. It identifies what the thread is waiting for; the expression in the while loop determines whether waiting is still necessary. The thread can leave the loop when an iteration needs accounting or the application has finished. section 20.5

A condition variable models a processing dependency between threads. In the same application, separate variables coordinate accounting, updating, and drawing. A thread waits when its shared-state test says there is no work; another thread signals a condition variable after advancing the work. section 20.5 section 20.8

TAKEAWAY: A notification is not the condition expression.

  • Keep the wait inside a loop that tests shared state. A wait can return without the expression changing, even when the untimed interface is used. section 20.5
  • Include termination in the test when shutdown must let a waiting thread proceed; the accounting loop tests L->finished as well as the iteration counts. section 20.5
  • For a timed wait, supply the condition variable, mutex, and absolute time limit. The example computes a limit one second ahead, so a return may simply mean the wait timed out. section 20.5

The mutex protects the shared-state test and surrounding critical section. Entering cnd_wait or cnd_timedwait acts as a mutex release; returning from either acts as a mutex acquisition. Critical sections protected by that mutex therefore see the effects of earlier critical sections. Prefer cnd_timedwait over cnd_wait when reasoning about deadlock: a timed wait need not remain blocked indefinitely, though checking a program’s overall liveness still requires care. section 21.2 section 20.7

Listing 20.2 The account thread function of B9
int account_thread(void* Lv) {
  life*restrict L = Lv;
  while (!L->finished) {
    // Blocks until there is work
    mtx_lock(&L->mtx);
    while (!L->finished && (L->accounted == L->iteration))
      life_wait(&L->acco, &L->mtx);

    // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
    life_account(L);
    if ((L->last + repetition) < L->accounted) {
      L->finished = true;
    }
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    cnd_signal(&L->upda);
    mtx_unlock(&L->mtx);
  }
  return 0;
}

The accounting-thread listing shows the mutex lock, repeated predicate test, wait, signal to the update thread, and mutex unlock in one control flow. section 20.5

How Modern C, Third Edition covers it

How deep it goes
Works through guarded wait loops, timed returns, signaling, and mutex synchronization; treats full liveness verification as a separate challenge.
Examples
Uses accounting, update, and draw threads with separate condition variables and shared iteration, completion, and drawing state.
What it assumes
Assumes familiarity with threads and mutex-protected shared state while distinguishing a condition variable from its condition expression.
What you can do after
Apply it via the C threads API by guarding waits with shared-state tests and rechecking those tests after return.
How it connects
Connects condition variables to mutexes, critical sections, timed waits, synchronization, deadlock, and liveness.

Also explained in

Compare how 7 books explain condition variable →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C, Third Edition. The section links point to the parts of the book it draws on.