Locks, Mutexes, Semaphores & Spinlocks Lab (Interactive)
Drive a shared cache with tunable read ratio and pick mutex, RWLock, semaphore, or spinlock. An analytic contention model showing how each primitive’s parallelism, waiters, and CPU waste respond to read-heavy or long-held critical sections.
Synchronization Primitives Throughput Lab
Same cache workload, four primitives: Mutex, Read-Write Lock, Counting Semaphore, Spinlock — pick the one that scales.
Context switch ≈ 2,000 μs+ — spinlocks only pay off below it.
Futex insight: uncontended mutex acquisition is a user-space CAS (~15 ns); the kernel sleep path (~2 μs+) only triggers on contention. Never hold any lock across a network RPC — a 500 ms downstream call freezes every worker.
How It Works Under the Hood
Mutexes serialize every access; futexes keep the uncontended path in user space (~15ns CAS) and only sleep in the kernel on contention. RWLocks let unlimited readers proceed in parallel, ideal for 99:1 read caches, but risk writer starvation without writer preference. Counting semaphores cap concurrency to p permits — the connection-pool pattern (HikariCP) — while spinlocks busy-wait, optimal below a context switch’s cost and catastrophic above it.
Core Architectural Principles
- RWLock throughput scales with concurrent readers; exclusive writers still serialize.
- Semaphore permits bound in-flight operations, converting unbounded blocking into queued backpressure.
- Spinlocks trade kernel sleeps for 100% CPU burn — only sensible when hold time stays far below ~1 μs.
Match primitive to workload out loud: "config cache at 99% reads gets an RWMutex; DB connection pool gets a counting semaphore sized to cores; kernel short sections get spinlocks." Always add the golden rule: never hold an in-memory lock across a network RPC, or a 500ms downstream stall freezes every worker.
Stronger mutual exclusion guarantees mean less concurrency; the skill is choosing the weakest primitive that still protects the invariant.