Overview
The ConditionVariable class implements a simple condition variable for thread coordination. Threads can wait on a condition and be signaled to wake up when the condition might be satisfied. It maintains a FIFO queue to ensure fair wake-up ordering.
Constructor
name
string
default:"Condition"
Optional name for debugging and display purposes.const bufferFull = new ConditionVariable('BufferFull');
const bufferEmpty = new ConditionVariable('BufferEmpty');
Initial State:
name: Provided name or "Condition"
queue: []
Properties
The name of the condition variable, used for debugging and logging.
FIFO queue of threads that have called wait() and are sleeping on this condition.
Methods
wait(thread)
Adds a thread to the waiting queue for this condition.
The thread that will wait on this condition.
Behavior:
- Adds thread to the queue if not already present (prevents duplicates)
- Thread should be marked as blocked by the caller (typically a monitor)
- Does not return a value (void)
Source: conditionVariable.js:11
Note: This is a low-level primitive. In typical usage, a thread would:
- Release associated mutex/monitor lock
- Call
wait() to join the condition queue
- Block until signaled
- Reacquire lock when awakened
// Example: Thread waiting on condition
function waitForCondition(thread, condition) {
condition.wait(thread);
thread.state = 'blocked';
thread.blockedBy = condition;
}
signal()
Wakes up one thread (the oldest) waiting on this condition.
- Thread: The thread that was removed from the queue (should be woken up)
- null: No threads were waiting on this condition
Behavior:
- Returns
null if queue is empty (no threads waiting)
- Removes and returns the first thread in the queue (FIFO ordering)
- Caller is responsible for changing thread state to ready/running
Source: conditionVariable.js:17
// Example: Signaling one waiting thread
const awakenedThread = condition.signal();
if (awakenedThread) {
awakenedThread.state = 'ready';
awakenedThread.blockedBy = null;
console.log(`Woke up ${awakenedThread.name}`);
} else {
console.log('No threads waiting');
}
Usage Example
import { ConditionVariable } from './core/conditionVariable.js';
// Producer-Consumer scenario
const notEmpty = new ConditionVariable('NotEmpty');
const notFull = new ConditionVariable('NotFull');
let buffer = [];
const maxSize = 5;
// Producer
function produce(thread, item) {
if (buffer.length >= maxSize) {
// Buffer full - wait for space
notFull.wait(thread);
thread.state = 'blocked';
return false; // Can't produce yet
}
buffer.push(item);
// Wake up a consumer
const consumer = notEmpty.signal();
if (consumer) {
consumer.state = 'ready';
}
return true;
}
// Consumer
function consume(thread) {
if (buffer.length === 0) {
// Buffer empty - wait for items
notEmpty.wait(thread);
thread.state = 'blocked';
return null; // Can't consume yet
}
const item = buffer.shift();
// Wake up a producer
const producer = notFull.signal();
if (producer) {
producer.state = 'ready';
}
return item;
}
FIFO Wake-up Guarantee
The condition variable ensures FIFO ordering:
wait() adds threads to the end of the queue
signal() removes from the front of the queue
- First thread to wait is first to be signaled
// Sequence:
condition.wait(thread1); // queue: [thread1]
condition.wait(thread2); // queue: [thread1, thread2]
condition.wait(thread3); // queue: [thread1, thread2, thread3]
const first = condition.signal(); // Returns thread1
const second = condition.signal(); // Returns thread2
const third = condition.signal(); // Returns thread3
const none = condition.signal(); // Returns null
Typical Usage Pattern
Condition variables are typically used with a mutex/monitor:
// 1. Acquire lock
mutex.acquire(thread);
// 2. Check condition
while (!conditionMet()) {
// 3. Release lock and wait
mutex.release(thread);
condition.wait(thread);
thread.state = 'blocked';
// ... Thread sleeps here ...
// 4. Reacquire lock when signaled
mutex.acquire(thread);
}
// 5. Condition is true, do work
doWork();
// 6. Release lock
mutex.release(thread);
Signal vs Broadcast
This implementation provides signal (wake one thread) but not broadcast (wake all threads). For broadcast functionality, you would need to call signal() in a loop:
// Broadcast pattern (wake all waiting threads)
function broadcast(condition) {
const awakened = [];
let thread;
while ((thread = condition.signal()) !== null) {
thread.state = 'ready';
thread.blockedBy = null;
awakened.push(thread);
}
return awakened;
}