Understanding the Thread Synchronization Simulator’s engine and visualization layers
The Thread Synchronization Simulator follows a clean separation between the execution engine (core logic) and the UI layer (visualization). This architecture enables accurate simulation of concurrent behavior while providing rich visual feedback.
// From core/engine.jsexport class Engine { constructor() { this.threads = []; // Lista de hilos registrados this.tick = 0; // Ciclo global del simulador } addThread(thread) { this.threads.push(thread); } // Ejecuta un ciclo completo sobre todos los hilos step(context) { this.tick++; const logs = []; for (const thread of this.threads) { // Ignora hilos que no pueden avanzar if (thread.state === "blocked" || thread.state === "finished") continue; thread.state = "running"; const inst = thread.currentInstruction(); if (!inst) { thread.state = "finished"; continue; } // Ejecuta la instruccion y guarda mensaje para timeline const resultLog = this.execute(thread, inst, context); if (resultLog) logs.push(resultLog); } return logs; }}
Key Design Decision: The engine processes all ready threads in a single tick. This simulates concurrent execution while maintaining deterministic, reproducible behavior.
Threads are lightweight execution contexts with program counters:
// From core/thread.jsexport class Thread { constructor(name, instructions) { this.name = name; // Nombre visible en UI y logs this.instructions = instructions; // Lista ordenada de instrucciones this.pc = 0; // Indice de la instruccion actual this.state = "ready"; // ready, running, blocked, finished this.blockedBy = null; // Recurso que bloqueo el hilo } currentInstruction() { return this.instructions[this.pc]; } nextInstruction() { this.pc++; }}
// From core/mutex.jsexport class Mutex { constructor() { this.locked = false; this.owner = null; this.queue = []; // Cola FIFO para hilos bloqueados } acquire(thread) { // Transferencia de propiedad: el hilo ya posee el mutex if (this.locked && this.owner === thread) { return true; } // Mutex libre: asignacion directa if (!this.locked) { this.locked = true; this.owner = thread; return true; } // Mutex ocupado: bloquear hilo thread.state = "blocked"; thread.blockedBy = this; if (!this.queue.includes(thread)) { this.queue.push(thread); } return false; } release(thread) { if (this.owner !== thread) { throw new Error("Solo el poseedor puede liberar el mutex"); } if (this.queue.length === 0) { this.locked = false; this.owner = null; } else { // Transferencia directa para respetar FIFO const nextThread = this.queue.shift(); this.locked = true; nextThread.state = "ready"; nextThread.blockedBy = null; this.owner = nextThread; } }}
Critical Implementation Detail: The mutex transfers ownership directly to the next waiting thread without ever becoming “free”. This prevents race conditions in the queue.
User clicks “Paso” → UI.runTick() → engine.step(context)
// Engine iterates threads// Cliente-1: state=ready, pc=0, inst=ACQUIRE// → mutex.acquire(Cliente-1) succeeds// → Cliente-1.pc++ (now 1)// → Returns log: "🔑 Cliente-1 entro en Seccion Critica"// Cliente-2: state=ready, pc=0, inst=ACQUIRE// → mutex.acquire(Cliente-2) fails (mutex owned by Cliente-1)// → Cliente-2.state = "blocked"// → mutex.queue.push(Cliente-2)// → Returns log: "🔒 Cliente-2 esta esperando el Mutex"// Cliente-3: similar to Cliente-2
3
UI Update
// Logs added to timelinetimeline.addEvent("🔑 Cliente-1 entro en Seccion Critica");timeline.addEvent("🔒 Cliente-2 esta esperando el Mutex");// UI state refreshedUI.update(engine, context);// Displays: owner="Cliente-1", queue=[Cliente-2, Cliente-3]