Skip to main content

Overview

The Barrier class implements a cyclic synchronization barrier. All participating threads must arrive at the barrier before any can proceed. When the last thread arrives, the barrier opens, releasing all waiting threads, and then resets for potential reuse.

Constructor

participants
number
required
The number of threads that must arrive at the barrier before it opens. Minimum value is 1.
const barrier = new Barrier(3); // Wait for 3 threads
Initial State:
  • participants: Math.max(1, participants)
  • waitingQueue: []
  • arrived: 0
  • granted: new Set()

Properties

participants
number
The total number of threads required to open the barrier.
waitingQueue
Array<Thread>
FIFO queue of threads currently blocked waiting for the barrier to open.
arrived
number
Count of threads that have arrived in the current round (reset to 0 after barrier opens).
granted
Set<Thread>
Set of threads that have been granted permission to pass after the barrier opened.

Methods

arrive(thread)

Thread arrives at the barrier and waits for all others to arrive.
thread
Thread
required
The thread arriving at the barrier.
return
object
Object containing:
  • passed (boolean): true if thread can proceed, false if blocked
  • opened (boolean): true if this arrival opened the barrier (last thread)
  • released (Array<Thread>): List of threads released when barrier opened (empty if not opener)
Behavior:
  1. Already granted: If thread was previously granted permission, consumes grant and passes through
  2. Not all arrived: Increments arrived count, blocks thread, adds to queue
  3. Last arrives: Opens barrier, releases all waiting threads, resets counter, returns all released threads
Source: barrier.js:15
// Example: Thread arriving at barrier
const result = barrier.arrive(thread1);

if (result.passed) {
  if (result.opened) {
    console.log('Last thread - barrier opened!');
    console.log(`Released ${result.released.length} threads`);
  } else {
    console.log('Thread passed (was previously waiting)');
  }
} else {
  console.log('Thread blocked at barrier');
}

Usage Example

import { Barrier } from './core/barrier.js';

// Three threads must synchronize
const checkpoint = new Barrier(3);

// Thread 1
function thread1Code() {
  console.log('Thread 1: Starting work');
  doWork();
  
  const result = checkpoint.arrive(thread1);
  if (!result.passed) {
    console.log('Thread 1: Waiting at barrier');
    return; // Blocked
  }
  
  console.log('Thread 1: Continuing after barrier');
}

// Thread 2
function thread2Code() {
  console.log('Thread 2: Starting work');
  doWork();
  
  const result = checkpoint.arrive(thread2);
  if (!result.passed) {
    console.log('Thread 2: Waiting at barrier');
    return; // Blocked
  }
  
  console.log('Thread 2: Continuing after barrier');
}

// Thread 3 (last to arrive)
function thread3Code() {
  console.log('Thread 3: Starting work');
  doWork();
  
  const result = checkpoint.arrive(thread3);
  // This one opens the barrier
  console.log(`Thread 3: Opened barrier, released ${result.released.length} threads`);
  console.log('Thread 3: Continuing after barrier');
}

Cyclic Behavior

The barrier is cyclic - it automatically resets after opening:
const barrier = new Barrier(2);

// Round 1
barrier.arrive(thread1); // arrived = 1, blocked
barrier.arrive(thread2); // arrived = 0 (reset!), barrier opens

// Round 2 - can be reused
barrier.arrive(thread1); // arrived = 1, blocked
barrier.arrive(thread2); // arrived = 0 (reset!), barrier opens
The counter resets to 0 when the last thread arrives:
// Source: barrier.js:42
this.arrived = 0; // Reset for next round

Opening Sequence

When the last thread arrives, the barrier:
  1. Releases all waiting threads from the queue
  2. Grants each thread permission to pass
  3. Changes their state from "blocked" to "ready"
  4. Resets the arrived counter to 0
  5. Returns the list of released threads
// Last thread arrives
if (this.arrived === this.participants) {
  const released = [];
  
  // Release all waiting threads
  while (this.waitingQueue.length > 0) {
    const waitingThread = this.waitingQueue.shift();
    waitingThread.state = "ready";
    waitingThread.blockedBy = null;
    this.granted.add(waitingThread);
    released.push(waitingThread);
  }
  
  this.arrived = 0; // Reset
  return { passed: true, opened: true, released };
}

Grant Mechanism

Threads that were waiting receive a “grant” when released:
// When barrier opens
this.granted.add(waitingThread);

// When thread runs arrive() again
if (this.granted.has(thread)) {
  this.granted.delete(thread); // Consume grant
  return { passed: true, opened: false, released: [] };
}
This ensures that when a waiting thread is scheduled to run again, it can immediately pass through without re-checking the barrier condition.

Synchronization Pattern

const barrier = new Barrier(4);

// Four threads doing parallel work
function parallelPhase1() {
  // Each thread does independent work
  processData();
}

function synchronizationPoint(thread) {
  const result = barrier.arrive(thread);
  
  if (!result.passed) {
    // Thread blocked - will resume here when barrier opens
    return false;
  }
  
  return true; // Can proceed to phase 2
}

function parallelPhase2() {
  // All threads synchronized - continue with next phase
  processMoreData();
}

Timeline Example

const barrier = new Barrier(3);

// Time 0: Thread A arrives
barrier.arrive(threadA);
// arrived = 1, threadA blocked, queue = [threadA]

// Time 1: Thread B arrives  
barrier.arrive(threadB);
// arrived = 2, threadB blocked, queue = [threadA, threadB]

// Time 2: Thread C arrives (LAST!)
const result = barrier.arrive(threadC);
// result.opened = true
// result.released = [threadA, threadB]
// threadA.state = "ready"
// threadB.state = "ready"
// threadC.state = "running" (never blocked)
// arrived = 0 (reset)
// queue = [] (empty)

// Time 3: All three threads continue past barrier

Use Cases

Barriers are ideal for:
  1. Phased parallel algorithms: All threads must complete phase N before any starts phase N+1
  2. Data parallel processing: Wait until all workers finish processing their chunk
  3. Checkpoint synchronization: Ensure all threads reach a checkpoint before proceeding
  4. Iterative algorithms: Synchronize at the end of each iteration
// Example: Parallel matrix computation
const workers = 4;
const barrier = new Barrier(workers);

function workerThread(workerId) {
  for (let iteration = 0; iteration < 10; iteration++) {
    // Each worker processes its portion
    processMatrixChunk(workerId);
    
    // Wait for all workers to finish this iteration
    const result = barrier.arrive(thread);
    if (!result.passed) {
      return; // Blocked - will resume at next iteration
    }
  }
}