Peterson’s Algorithm is a classic software-based solution for mutual exclusion between exactly two threads. Unlike mutexes and semaphores which typically rely on hardware primitives, Peterson’s algorithm achieves synchronization using only shared memory reads and writes.
Understanding fundamental principles of concurrent programming
Two-Thread Systems
Systems with exactly two competing threads (no more, no less)
Hardware Constraints
Platforms without atomic hardware instructions
Historical Study
Learning the evolution of synchronization algorithms
Modern Usage: Peterson’s algorithm is rarely used in modern systems due to memory ordering issues on contemporary processors. Modern systems use hardware-supported primitives (atomic operations, mutexes). This implementation is for educational purposes.
Here’s the complete Peterson’s lock implementation from the simulator:
source/js/core/petersonLock.js
// Algoritmo de Peterson para exclusion mutua de 2 hilos/robots.export class PetersonLock { constructor() { // flag[i] = true cuando el robot i quiere entrar a seccion critica. this.flag = [false, false]; // turn define quien cede paso en caso de conflicto. this.turn = 0; // Para visualizacion: dueño actual de la estacion. this.owner = null; } // Intenta tomar lock para robot i (0 o 1). Devuelve true si puede entrar. lock(i) { const other = 1 - i; this.flag[i] = true; this.turn = other; // Condicion de espera activa de Peterson. if (this.flag[other] && this.turn === other) { return false; } this.owner = i; return true; } // Libera lock del robot i. unlock(i) { this.flag[i] = false; if (this.owner === i) this.owner = null; }}
// Thread 0 executes firstflag[0] = true;turn = 1;// Check: flag[1] && turn == 1?// flag[1] still false → Thread 0 enters// Thread 1 executes laterflag[1] = true;turn = 0;// Check: flag[0] && turn == 0?// flag[0] is true AND turn is 0// → Thread 1 must wait
Result: First thread enters, second thread waits.
Scenario 3: Both threads want in - race condition
// Both threads execute simultaneouslyflag[0] = true; // Thread 0flag[1] = true; // Thread 1turn = 1; // Thread 0 sets turnturn = 0; // Thread 1 sets turn (overwrites!)// Thread 0 checks: flag[1] && turn == 1?// flag[1] is true, but turn is 0 (not 1)// → Thread 0 enters// Thread 1 checks: flag[0] && turn == 0?// flag[0] is true AND turn is 0// → Thread 1 waits
Result: The thread that writes turn last waits. The other enters.
Key insight: The turn variable acts as a tiebreaker. When both threads want in, whoever writes to turn last will wait. This guarantees mutual exclusion.
Mutual Exclusion: At most one thread in critical section
Proof by contradiction:Assume both Thread 0 and Thread 1 are in critical section.For Thread 0 to enter: NOT (flag[1] && turn == 1) → Either flag[1] == false OR turn == 0For Thread 1 to enter: NOT (flag[0] && turn == 0) → Either flag[0] == false OR turn == 1But we know both flag[0] and flag[1] are true (both declared intent).Therefore: Thread 0 entered means turn == 0 Thread 1 entered means turn == 1Contradiction! turn cannot be both 0 and 1.Therefore, both cannot be in critical section.
Progress: If no thread in critical section, one will eventually enterBounded Waiting: A waiting thread will enter in finite time
Exactly Two Threads: Peterson’s algorithm only works for exactly 2 threads. For N threads, you need generalized algorithms or hardware primitives.
Memory Ordering: On modern processors with weak memory models, Peterson’s algorithm requires memory barriers to work correctly. Simple reads/writes may be reordered by the CPU or compiler.
// Modern C++ would need:flag[i].store(true, std::memory_order_seq_cst);turn.store(other, std::memory_order_seq_cst);
Busy Waiting: Wastes CPU cycles spinning. Not suitable for long critical sections or systems with limited CPU resources.