v133 · javascript

Hybrid spin+wait mutex

The TC39-recommended pattern: spin a few iterations with Atomics.pause() (cheap), then fall back to Atomics.wait() (yields the OS thread). Simulates two workers contending for a single lock and shows how spin budget affects spin count, wait count, and total acquisition time.

Atomics.pause: checking…
Pure spin (no pause, no wait) burns CPU
while (Atomics.compareExchange( lock, 0, 0, 1) !== 0) { // tight loop — no hint, no yield // hammers cache line under contention }
Lock acquisitions with many retries create cache-line contention. No CPU hint means the pipeline re-executes speculatively on every iteration.
Hybrid: spin → pause → wait TC39 recommended
let n = 0; while (Atomics.compareExchange( lock, 0, 0, 1) !== 0) { if (n < MAX_SPIN) { Atomics.pause(n++); // hint: back off } else { Atomics.wait(lock, 1); // yield thread n = 0; } }
Spin budget burns through hot path quickly. Pause reduces contention. Falling back to wait() yields the OS thread to avoid starving other workers.
Simulation settings
Workers contending
Spin budget (iterations)
Lock hold time (ms)
Acquisitions
Total acquisitions
Spin iterations
pause() calls
Wait calls
Atomics.wait() fallback
Avg contention
retries per lock
Worker activity breakdown
Run simulation to see worker phases.
Lock events
Events will appear here after running the simulation.
// Hybrid spin+wait mutex — TC39 recommended pattern
// In a SharedWorker / Worker with a SharedArrayBuffer:
const lock = new Int32Array(new SharedArrayBuffer(4));

function acquire(maxSpin = 10) {
  let n = 0;
  while (Atomics.compareExchange(lock, 0, 0, 1) !== 0) {
    if (n < maxSpin) {
      Atomics.pause(n++);     // hint CPU to back off; arg = iteration count
    } else {
      Atomics.wait(lock, 1);  // yield OS thread until value changes
      n = 0;                  // reset spin budget after waking
    }
  }
}

function release() {
  Atomics.store(lock, 0, 0);
  Atomics.notify(lock, 0, 1);  // wake one waiting worker
}

// Rule of thumb for MAX_SPIN:
//   short critical sections (< 1µs work) → spin 10–50 iterations
//   medium critical sections             → spin 5–10 iterations
//   long work inside the lock            → spin 0 (go straight to wait)

see also

references