Condition Variables in OS

3
0

Introduction

Condition variables solve a specific waiting problem in synchronization. A thread may hold the mutex for shared data, but still be unable to continue because the required state is not ready.

For example, a consumer may lock a queue and find it empty. Instead of repeatedly checking and wasting CPU, it should sleep until the queue may contain an item.

Why Condition Variables Exist

A mutex protects shared state, but it does not tell a thread when the state becomes useful. It only controls safe access to that state.

A condition variable gives threads a waiting place. It lets a thread sleep while waiting for a shared-state condition, such as “queue is not empty” or “buffer is not full.”

Condition variables do not replace mutual exclusion. They work together with a mutex.

Predicate, Mutex, and Condition Variable

The condition being waited for is often called a predicate. A predicate is a true-or-false statement about shared state.

Part

Role

Predicate

The shared-state condition, such as queue is not empty

Mutex

Protects checking and changing the shared state

Condition variable

Lets threads sleep and wake around that condition

The condition variable is not the condition itself. It is the waiting mechanism for threads that cannot proceed until the predicate may be true.

Common Operations

Condition variables usually have three main operations.

Operation

Meaning

wait

Put the thread to sleep until the condition may be true

signal

Wake one waiting thread

broadcast

Wake all waiting threads

The phrase “may be true” matters. A wakeup does not guarantee the predicate is still true when the awakened thread actually runs.

Why Wait Needs a Mutex

A thread must check the shared predicate while holding the mutex. If the predicate is false, it calls wait(cv, mutex).

The basic wait flow is:

  • Lock mutex: The thread safely checks shared state.

  • Check predicate: If the condition is false, it cannot continue.

  • Call wait: wait(cv, mutex) releases the mutex and sleeps atomically.

  • Wake later: The thread wakes when signaled or broadcast.

  • Reacquire mutex: Before returning from wait, it gets the mutex again.

  • Check again: The thread rechecks the predicate before proceeding.

The atomic release-and-sleep step prevents the dangerous gap where a signal could happen after the mutex is released but before the thread actually sleeps.

Why Wait Needs a Mutex

Why Wait Needs a Mutex

The While-Loop Rule

A waiting thread should use a while loop, not a single if.

lock(mutex)

while condition_is_false:
    wait(cv, mutex)

use shared state

unlock(mutex)

The loop is necessary because waking up does not prove the condition is still true. Another thread may consume the resource first, or the thread may wake spuriously.

The safe mental sequence is: check condition, wait, wake, recheck condition, then proceed.

Producer-Consumer Example

In the producer-consumer problem, condition variables are commonly used with a bounded buffer.

  • mutex: Protects the shared buffer state

  • not_full: Producers wait here when the buffer is full

  • not_empty: Consumers wait here when the buffer is empty

A producer locks the mutex and checks whether the buffer is full. If it is full, the producer waits on not_full. When it wakes, it rechecks the condition and adds an item only if space exists.

A consumer locks the mutex and checks whether the buffer is empty. If it is empty, the consumer waits on not_empty. When it wakes, it rechecks the condition and removes an item only if an item exists.

Producer-Consumer Example

Producer-Consumer Example

Signal vs Broadcast

signal wakes one waiting thread. It is useful when one state change can help one waiter, such as adding one item to a queue.

broadcast wakes all waiting threads. It is useful when many waiters may need to recheck the state, or when the program cannot easily know which waiter should wake.

Broadcast can cause extra wakeups, but that is safe if every waiter uses the while-loop rule and rechecks the predicate.

Missed Wakeup Problem

A missed wakeup happens when a signal occurs before the thread is actually waiting. The signal wakes no one, and the thread may later sleep forever.

Correct condition-variable use avoids this pattern:

  • Check state with the mutex held.

  • Use wait so releasing the mutex and sleeping happen atomically.

  • Change shared state while holding the same mutex.

  • Signal or broadcast after updating the state.

This pairing is why condition variables are tied to mutex-protected shared state.

Missed Wakeup Problem

Missed Wakeup Problem

Summary

A condition variable lets threads wait until a shared-state condition may become true. The mutex protects the predicate, while the condition variable coordinates sleeping and waking.

The most important rule is to wait in a while loop. A signal or broadcast only means the condition may be true, so every awakened thread must reacquire the mutex and recheck the predicate.

CS Core

Read Similar Blogs

Comments0