Skip to main content

Overview

The Semaphore class implements a counting semaphore with FIFO queue management and resource assignment tracking. It’s designed for scenarios like managing access to multiple printers, where each thread needs exclusive access to one resource from a pool.

Constructor

initialCount
number
required
The initial count representing the number of available resources. Must be non-negative.
const semaphore = new Semaphore(3); // 3 available resources
Initial State:
  • count: Math.max(0, initialCount)
  • queue: []
  • assignments: new Map()

Properties

count
number
The number of available resources. Represents how many more threads can enter the protected region.
queue
Array<Thread>
FIFO queue of blocked threads waiting for a resource to become available.
assignments
Map<Thread, number>
Maps each thread to its assigned printer/resource index. Used to track which resource belongs to which thread.

Methods

wait(thread, printers)

Attempts to acquire a semaphore token and assign a printer resource to the thread.
thread
Thread
required
The thread attempting to acquire a resource.
printers
Array<Printer>
required
Array of printer objects, each with an owner property.
return
number | null
  • number: Index of the assigned printer (0-based)
  • null: No resource available, thread is blocked
Behavior:
  1. Already assigned: If thread already has a printer (from transfer), returns that printer index
  2. Resource available (count > 0): Finds first available printer, decrements count, assigns printer, returns index
  3. No resources: Blocks thread, adds to queue, returns null
Source: semaphore.js:13
// Example: Thread waiting for printer
const printerIndex = semaphore.wait(thread1, printers);
if (printerIndex !== null) {
  console.log(`Thread got printer ${printerIndex}`);
  // Use the printer
} else {
  console.log('Thread blocked, waiting for printer');
}

signal(thread, printers)

Releases the thread’s printer resource and transfers it to the next waiting thread if available.
thread
Thread
required
The thread releasing its resource. Must have an assigned printer.
printers
Array<Printer>
required
Array of printer objects to update ownership.
return
object
Object containing:
  • releasedPrinter (number): Index of the printer that was released
  • nextThread (Thread | null): The thread that received the printer, or null if no waiters
Behavior:
  1. Validation: Throws error if thread has no assigned resource
  2. Release resource: Removes assignment, clears printer owner
  3. Has waiters: Directly transfers the same printer to first thread in queue
  4. No waiters: Increments count (resource returned to pool)
Source: semaphore.js:41 Throws:
  • Error: “El hilo no tiene recurso asignado para signal” if thread calls signal without a resource
// Example: Releasing printer
try {
  const result = semaphore.signal(thread1, printers);
  console.log(`Released printer ${result.releasedPrinter}`);
  
  if (result.nextThread) {
    console.log(`Transferred to ${result.nextThread.name}`);
  } else {
    console.log('No waiting threads, resource returned to pool');
  }
} catch (error) {
  console.error('Signal error:', error.message);
}

getAssignedPrinter(thread)

Returns the printer index assigned to a thread.
thread
Thread
required
The thread to query.
return
number | null
The printer index (0-based) if thread has an assignment, otherwise null.
Source: semaphore.js:70
// Example: Checking assignment
const printerIndex = semaphore.getAssignedPrinter(thread1);
if (printerIndex !== null) {
  console.log(`Thread is using printer ${printerIndex}`);
}

Usage Example

import { Semaphore } from './core/semaphore.js';

// Initialize with 2 printers
const printers = [
  { owner: null },
  { owner: null }
];
const semaphore = new Semaphore(2);

// Thread tries to print
function threadCode(thread) {
  const printerIndex = semaphore.wait(thread, printers);
  
  if (printerIndex !== null) {
    // Got a printer - use it
    console.log(`${thread.name} using printer ${printerIndex}`);
    
    // Simulate printing
    setTimeout(() => {
      // Release printer when done
      const result = semaphore.signal(thread, printers);
      console.log(`${thread.name} released printer ${result.releasedPrinter}`);
    }, 1000);
  }
}

Direct Transfer Mechanism

When a thread releases a printer, if there are waiting threads, the semaphore directly transfers the same printer resource:
// Direct transfer on signal
if (this.queue.length > 0) {
  const nextThread = this.queue.shift();
  nextThread.state = "ready";
  // Transfer same printer without incrementing count
  this.assignments.set(nextThread, releasedPrinter);
  printers[releasedPrinter].owner = nextThread;
}
This ensures:
  • FIFO fairness (first in queue gets resource)
  • Immediate resource reuse
  • No race conditions

Counting vs Assignment

The semaphore maintains both:
  • count: Logical number of available slots
  • assignments: Physical mapping of threads to specific resources
This dual tracking enables:
  1. Proper blocking/unblocking based on availability
  2. Resource-specific assignment (which printer a thread is using)
  3. Clean transfer of exact resources between threads