Engine Class
The Engine class is the heart of the simulator, responsible for managing threads and executing instructions in a tick-based cycle.
Constructor
Creates a new execution engine instance.
const engine = new Engine();
Properties
List of registered threads that will be executed
Global cycle counter that increments with each step
Methods
addThread()
Registers a thread for execution in the engine.
The thread instance to add to the execution queue
const thread = new Thread("Client-1", [
{ type: Instructions.ACQUIRE },
{ type: Instructions.WITHDRAW, amount: 100 },
{ type: Instructions.RELEASE }
]);
engine.addThread(thread);
step()
Executes one complete cycle across all threads. This is the main execution loop.
Scenario-specific context object containing shared resources
Array of log messages generated during execution
step(context) {
this.tick++;
const logs = [];
for (const thread of this.threads) {
// Skip threads that cannot advance
if (thread.state === "blocked" || thread.state === "finished") continue;
thread.state = "running";
const inst = thread.currentInstruction();
// Mark as finished if no instructions remain
if (!inst) {
thread.state = "finished";
continue;
}
// Execute instruction and collect log message
const resultLog = this.execute(thread, inst, context);
if (resultLog) logs.push(resultLog);
}
return logs;
}
execute()
Executes a single instruction for a specific thread. This method contains the dispatcher logic for all instruction types.
The thread executing the instruction
The instruction object with type and optional parameters
Context object with shared resources (mutex, account, semaphore, etc.)
Human-readable message describing what happened during execution
Context Objects
The context parameter varies by scenario:
Mutex Scenario
{
mutex: Mutex, // Binary lock
account: { // Shared bank account
balance: number
}
}
Semaphore Scenario
{
semaphore: Semaphore, // Counting semaphore
printers: Array<{ // Printer pool
owner: Thread | null,
completedJobs: number,
totalPages: number
}>
}
Condition Variable Scenario
{
restaurant: {
availableDishes: number,
totalCooked: number,
totalEaten: number,
foodCondition: ConditionVariable
}
}
Execution Example
import { Engine } from "./core/engine.js";
import { Thread } from "./core/thread.js";
import { Mutex } from "./core/mutex.js";
import { Instructions } from "./core/instructions.js";
// Create engine
const engine = new Engine();
// Create scenario context
const context = {
mutex: new Mutex(),
account: { balance: 1000 }
};
// Create threads
const thread1 = new Thread("Client-1", [
{ type: Instructions.ACQUIRE },
{ type: Instructions.WITHDRAW, amount: 100 },
{ type: Instructions.RELEASE }
]);
const thread2 = new Thread("Client-2", [
{ type: Instructions.ACQUIRE },
{ type: Instructions.DEPOSIT, amount: 50 },
{ type: Instructions.RELEASE }
]);
engine.addThread(thread1);
engine.addThread(thread2);
// Execute steps
const logs1 = engine.step(context);
console.log(logs1); // ["🔑 Client-1 entro en Seccion Critica", ...]
const logs2 = engine.step(context);
console.log(logs2); // ["💸 Client-1 modifico el saldo: -$100", ...]
Instruction Execution
The execute() method uses a large switch statement to handle different instruction types:
switch (inst.type) {
case Instructions.ACQUIRE:
if (mutex.acquire(thread)) {
thread.nextInstruction();
return `🔑 ${thread.name} entro en Seccion Critica`;
}
return `🔒 ${thread.name} esta esperando el Mutex`;
case Instructions.WITHDRAW:
const requested = Number(inst.amount) || 0;
const allowed = Math.min(requested, Math.max(0, account.balance));
account.balance -= allowed;
thread.nextInstruction();
return `💸 ${thread.name} modifico el saldo: -$${allowed}`;
case Instructions.RELEASE:
mutex.release(thread);
thread.nextInstruction();
return `🔓 ${thread.name} salio y libero el Mutex`;
// ... 26 more instruction types
}
Thread Scheduling
The engine uses a simple round-robin scheduler:
- Increment global tick counter
- Iterate through all threads
- Skip threads that are
blocked or finished
- Set thread state to
running
- Get current instruction via
thread.currentInstruction()
- Execute instruction via
execute()
- Collect log messages
- Return logs for UI rendering
Only threads in ready state will execute. Blocked threads remain in their queues until released by synchronization primitives.