Free preview

Why this matters: a spinlock's design is one atomic word and a loop — which means the design content is entirely in two arguments: the ordering argument that makes the lock actually protect anything, and the coherence argument that makes it performant. Both are "in words" arguments. Get them right here and the code writes itself.

The state: one word

locked: atomic word     0 = free, 1 = held

Acquiring means atomically flipping 0 to 1 and knowing you were the one who did it — which is exactly what atomic exchange provides: exchange(locked, 1) sets the word to 1 and hands back what it was. Got 0 back? The lock was free and is now yours. Got 1? Someone else holds it; try again. That loop — exchange until you get a 0 — is test-and-set (TAS), the primal spinlock. Releasing is storing 0.

Mutual exclusion follows from the atomicity of the exchange: two threads can both call it, but the hardware serializes them — exactly one receives the 0. State the invariant plainly:

1. at most one thread has observed exchange() -> 0 without yet
   storing 0 back           (that thread is "the holder")
2. only the holder stores 0 (unlock)
3. everything the holder did inside the section is visible to
   the NEXT holder          (the ordering contract, below)

The ordering contract: what makes a lock a lock

Invariant 3 is the one candidates forget, and it's the difference between "a flag" and "a lock." Mutual exclusion alone only means threads take turns; it doesn't by itself mean each thread sees what the previous turn did. A lock's entire purpose is that the critical section's writes travel with it.

The contract, in the acquire/release vocabulary the requirements gave us:

lock():    the winning exchange must have ACQUIRE semantics —
           nothing from the section may be reordered before it,
           and it synchronizes-with the previous unlock

unlock():  the store of 0 must have RELEASE semantics —
           every write from the section is visible to whoever
           acquires next; nothing from the section drifts after it

Say the pairing in words: release on unlock publishes the section; acquire on lock observes it. It's the same release/acquire handshake the ring buffer used to publish records — there it carried one slot, here it carries the entire critical section. A lock with relaxed ordering is mutual exclusion around data races: threads politely take turns corrupting each other.

The performance argument: spinning versus the coherence protocol

Here is where the hardware model from lesson 01 earns its keep. The naive TAS loop retries exchange back-to-back. The problem: exchange is a read-modify-write, and on an invalidation-based coherence protocol, an RMW needs the cache line in exclusive state. Every spinning thread, on every iteration, demands exclusive ownership of the same line — so the line ping-pongs between cores, and each bounce invalidates everyone else's copy. A dozen waiters generate a storm of coherence traffic that slows the holder too: the holder's own unlock must fight through the same saturated line. The waiting threads are actively delaying the thing they're waiting for.

The fix is embarrassingly small and entirely conceptual — spin on a read, not on the RMW:

TTAS (test-and-test-and-set):
    loop:
        while (load(locked) == 1): spin      // plain read — SHARED copy
        if (exchange(locked, 1) == 0): done  // RMW only when it might win

While the lock is held, every waiter spins on a plain load of its own cached copy of the line, in shared state — zero traffic. When the holder releases, one invalidation goes out, everyone re-reads, and only then do the waiters attempt the expensive exchange. The line bounces once per handoff instead of continuously. Same primitives, same lock — the entire improvement is knowing what the coherence protocol charges for.

That argument — RMW spin ping-pongs an exclusive line; read-spin waits quietly on a shared copy — is the centerpiece of this interview. It's worth rehearsing until you can deliver it in four sentences.

Backoff: don't stampede the handoff

TTAS still has a thundering-herd moment: at release, all waiters see the 0 simultaneously and all attempt the exchange; one wins and the rest re-enter the spin. With many waiters this makes every handoff a small traffic spike. Exponential backoff blunts it: after a failed attempt, pause before re-checking — briefly at first, doubling toward a cap. Fewer simultaneous attackers per release, less traffic, and (a point for lesson 04) the pauses are where CPU-politeness instructions belong.

The cost, stated honestly: backoff trades latency-of-acquisition for total traffic — a backed-off waiter may find the lock free and be caught napping. Cap the backoff and the trade stays sane.

What we rejected, and why

CAS instead of exchange for the acquire. cas(locked, 0, 1) also works, and on a failed compare some hardware avoids the write — a modest cousin of the TTAS insight. Exchange-based TTAS is the cleaner teaching design; name CAS as an equivalent so the choice looks like a choice.

A try_lock built separately. It's the TTAS attempt without the loop — one load, maybe one exchange, return the verdict. Free to add, worth mentioning, changes nothing structural.

Spinning forever without a plan. The design above assumes the short-sections requirement. If sections can block or run long, no amount of spin discipline saves you — the honest move is a different tool, which is lesson 04's territory.

Key takeaway

A spinlock is one atomic word plus two arguments. The ordering argument: acquire on the winning exchange, release on the unlock — the handshake that makes the critical section's writes travel with the lock. The coherence argument: spinning on the RMW ping-pongs an exclusive cache line and slows the holder itself; TTAS spins on a plain read of a shared copy and pays traffic only at handoff, with exponential backoff blunting the stampede. Deliver both arguments in words and the dozen lines of code are a formality.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue