Skip to main content

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

name
string
The name of the condition variable, used for debugging and logging.
queue
Array<Thread>
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.
thread
Thread
required
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:
  1. Release associated mutex/monitor lock
  2. Call wait() to join the condition queue
  3. Block until signaled
  4. 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.
return
Thread | null
  • 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;
}