The Herlihy Doctrine
Named for Maurice Herlihy, who founded wait-free synchronization. This skill
turns "everything atomic" from a slogan into a procedure grounded in his
published results — so every atomicize/rewrite picks the sound primitive, has
a provable correctness point, claims the true progress guarantee, and never
chases something topology forbids.
Before writing the concurrent code, invoke read-the-damn-docs for the exact
atomic API in play (Rust std::sync::atomic orderings, CUDA atomicCAS/
atomicAdd semantics, the memory model). A wrong memory ordering is a
Heisenbug that a parity gate catches only sometimes — ground it in the docs.
The scholarship (verified primary sources, with the NOT-Herlihy flags) lives in
references/corpus.md. Read it before citing any result — misattributing a
technique to Herlihy is the exact overclaim this skill exists to prevent.
The core move: constraint → atomicize
Given a constraint (a lock, a blocking wait, a serialization/step-barrier),
rewrite it atomic by running these seven rules in order. Each rule is
grounded in a specific Herlihy result — cite it, don't assert it.
Name the constraint's consensus need. Does the rewrite require multi-party
agreement — who wins a slot, who commits, who retires exactly once? If yes,
the primitive floor is CAS. (Consensus-number hierarchy — Herlihy,
"Wait-Free Synchronization", TOPLAS 1991.)
Pick the minimum sound primitive. Monotonic reservation / counters →
fetch-add (consensus number 2). Any contended commit or reclaim → CAS
(consensus number ∞). Never fetch-add where agreement is needed — two
"winners" is a latent correctness bug. Never CAS a pure counter — a wasted
retry loop. (Herlihy 1991.)
Check the impossibility wall first. A CAS-free (read/write/fetch-add-only)
fast path for any contended commit is provably impossible wait-free.
Don't burn a run inventing one. CAS (∞) is exactly the primitive that buys you
out of the register wall. (Topological computability — Herlihy & Shavit,
JACM 1999, Gödel Prize.)
Fix the linearization point = the single atomic step. Every fast path gets
exactly one instant — the successful CAS or the release-store — where its
effect becomes visible. A reader seeing old state must see a legal
pre-commit history. Linearizability is local: prove each object at its
point, and whole-engine correctness composes for free. (Herlihy & Wing,
TOPLAS 1990.)
Choose the progress guarantee by whether a token can wait. Token-flow
paths target wait-free. But upgrade a lock-free path to wait-free (via
helping) only where measured starvation exists — helping costs an
announce-scan on every op. Most paths at low concurrency are fine lock-free.
(Universal construction / helping — Herlihy 1991; progress lattice — Herlihy
& Shavit, OPODIS 2011.)
Reduce multi-word to one word before reaching for transactions. Prefer a
version-pointer / index swap that makes the commit a single CAS
(wait-free) over STM/HTM (only lock-free — abort/retry, livelock-prone, and
there is no HTM on the GPU decode path). (Transactional Memory — Herlihy &
Moss, ISCA 1993 — as a design lens, not a hot-path kernel.)
Pick reclamation by reader lifetime. Bounded, non-stalling readers (GPU
decode kernels, one step long) → epoch-based reclamation (tick an epoch
per step, free blocks retired ≥2 epochs back). Cite Pass-the-Buck as the
canonical safe-reclamation lineage; use its per-guard handoff only if
readers can stall unboundedly (not our case). (Repeat Offender / Pass-the-Buck
— Herlihy, Luchangco, Moir, DISC 2002, Dijkstra Prize 2022. EBR itself is
Fraser 2004 — NOT Herlihy.)
Per-subsystem map (LLM-inference)
Match the subsystem to the right result, progress guarantee, and linearization
point. This is the reference for atomic-inference; the shape generalizes.
| Subsystem |
Result to apply |
Progress |
Linearization point |
| KV cache slot commit |
Linearizability + CAS (∞) |
Wait-free |
Release-store of ready/tail |
| Scheduler / mid-step admission |
Universal construction (announce+help) |
Lock-free → wait-free if starved |
Winning CAS on running-set splice |
| MTP commit |
Single-CAS reduction (consensus ∞) |
Wait-free |
Successful committed_len CAS |
| KV-block reclamation |
Pass-the-Buck lineage → EBR |
Lock-free (reclaimer) |
Epoch-tick store |
| Request ring |
Michael-Scott queue (NOT Herlihy) |
Lock-free |
fetch-add slot index |
| On-device sampling |
Linearizability discipline |
Wait-free |
Single argmax CAS/store |
The honest hot-path caveat (read before you build)
Two Herlihy constructions are correctness references, not hot-path kernels:
- The universal construction (announce + consensus + help) proves any
sequential object can be made wait-free — but its O(n) announce-scan per op
serializes throughput. Borrow the helping idea and the proof, not the
literal construction. Ship a bespoke single-CAS / fetch-add structure.
- Transactional memory gives multi-word atomicity but pays abort/retry and
has no GPU-decode implementation. Reduce to one word instead.
Where a hand-rolled lock-free structure already gives the needed progress (a
Treiber stack for the free-list, an MS-queue for the ring — both not Herlihy,
see corpus.md), use it. The doctrine tells you which primitive is sound and
which guarantee you actually have — it is not a mandate to build the general
construction literally.
Audit rubric (labeling an existing path's true guarantee)
Cheap, high-value — run it before claiming any path is "lock-free":
- Single CAS / single release-store, bounded retries → genuinely wait-free.
- CAS-loop that can livelock under contention → only lock-free (or
obstruction-free) — do not call it wait-free.
- Any Mutex / RwLock / epoch barrier / blocking wait on the path → it is
blocking, not non-blocking. That is the constraint to rewrite.
Over-claiming a progress guarantee is the concurrency equivalent of an unscoped
"world-first" — the same honesty discipline applies. Label the true class, then
upgrade only the paths a token actually waits on.
The one-sentence version
Name what needs agreement, pick CAS if it does and fetch-add if it doesn't,
give the commit one visible instant, claim only the progress you can prove, free
memory by epoch, and never chase a wait-free path topology already ruled out.
1---2name: herlihy3description: Use when atomicizing any subsystem or removing a concurrency constraint — a lock, a Mutex/RwLock, a blocking wait, a serialization point, a step-barrier, a GIL-serialized scheduler. Triggers on "make this lock-free", "atomicize this", "rewrite this atomic", "which atomic primitive", "CAS or fetch-add", "is this actually wait-free / lock-free", "prove this concurrent path correct", "how do I safely reclaim this memory under lock-free readers", or designing a KV cache / scheduler / commit / free-list / ring on hardware atomics. This is the Herlihy doctrine: pick the primitive by consensus number, fix the linearization point, choose the progress guarantee by whether a token can wait, pick the reclamation scheme by reader lifetime, and know the impossibility walls before burning a run. Reach for it by default whenever a token-flow path must be non-blocking; skip only for genuinely single-threaded code with no shared mutable state.4---56# The Herlihy Doctrine78Named for Maurice Herlihy, who founded wait-free synchronization. This skill9turns "everything atomic" from a slogan into a procedure grounded in his10published results — so every atomicize/rewrite picks the *sound* primitive, has11a provable correctness point, claims the *true* progress guarantee, and never12chases something topology forbids.1314**Before writing the concurrent code, invoke `read-the-damn-docs`** for the exact15atomic API in play (Rust `std::sync::atomic` orderings, CUDA `atomicCAS`/16`atomicAdd` semantics, the memory model). A wrong memory ordering is a17Heisenbug that a parity gate catches only sometimes — ground it in the docs.1819The scholarship (verified primary sources, with the NOT-Herlihy flags) lives in20`references/corpus.md`. Read it before citing any result — misattributing a21technique to Herlihy is the exact overclaim this skill exists to prevent.2223## The core move: constraint → atomicize2425Given a constraint (a lock, a blocking wait, a serialization/step-barrier),26rewrite it atomic by running these seven rules **in order**. Each rule is27grounded in a specific Herlihy result — cite it, don't assert it.28291. **Name the constraint's consensus need.** Does the rewrite require *multi-party30 agreement* — who wins a slot, who commits, who retires exactly once? If yes,31 the primitive floor is **CAS**. *(Consensus-number hierarchy — Herlihy,32 "Wait-Free Synchronization", TOPLAS 1991.)*33342. **Pick the minimum sound primitive.** Monotonic reservation / counters →35 **fetch-add** (consensus number 2). Any contended commit or reclaim → **CAS**36 (consensus number ∞). Never fetch-add where agreement is needed — two37 "winners" is a latent correctness bug. Never CAS a pure counter — a wasted38 retry loop. *(Herlihy 1991.)*39403. **Check the impossibility wall first.** A CAS-free (read/write/fetch-add-only)41 fast path for any *contended commit* is **provably impossible** wait-free.42 Don't burn a run inventing one. CAS (∞) is exactly the primitive that buys you43 out of the register wall. *(Topological computability — Herlihy & Shavit,44 JACM 1999, Gödel Prize.)*45464. **Fix the linearization point = the single atomic step.** Every fast path gets47 exactly one instant — the successful CAS or the release-store — where its48 effect becomes visible. A reader seeing old state must see a *legal49 pre-commit* history. Linearizability is **local**: prove each object at its50 point, and whole-engine correctness composes for free. *(Herlihy & Wing,51 TOPLAS 1990.)*52535. **Choose the progress guarantee by whether a *token* can wait.** Token-flow54 paths target **wait-free**. But upgrade a lock-free path to wait-free (via55 helping) *only where measured starvation exists* — helping costs an56 announce-scan on every op. Most paths at low concurrency are fine lock-free.57 *(Universal construction / helping — Herlihy 1991; progress lattice — Herlihy58 & Shavit, OPODIS 2011.)*59606. **Reduce multi-word to one word before reaching for transactions.** Prefer a61 version-pointer / index swap that makes the commit a **single CAS**62 (wait-free) over STM/HTM (only lock-free — abort/retry, livelock-prone, and63 there is no HTM on the GPU decode path). *(Transactional Memory — Herlihy &64 Moss, ISCA 1993 — as a design lens, not a hot-path kernel.)*65667. **Pick reclamation by reader lifetime.** Bounded, non-stalling readers (GPU67 decode kernels, one step long) → **epoch-based reclamation** (tick an epoch68 per step, free blocks retired ≥2 epochs back). Cite Pass-the-Buck as the69 canonical safe-reclamation lineage; use its per-guard handoff *only* if70 readers can stall unboundedly (not our case). *(Repeat Offender / Pass-the-Buck71 — Herlihy, Luchangco, Moir, DISC 2002, Dijkstra Prize 2022. EBR itself is72 Fraser 2004 — NOT Herlihy.)*7374## Per-subsystem map (LLM-inference)7576Match the subsystem to the right result, progress guarantee, and linearization77point. This is the reference for atomic-inference; the *shape* generalizes.7879| Subsystem | Result to apply | Progress | Linearization point |80|---|---|---|---|81| KV cache slot commit | Linearizability + CAS (∞) | Wait-free | Release-store of `ready`/tail |82| Scheduler / mid-step admission | Universal construction (announce+help) | Lock-free → wait-free *if starved* | Winning CAS on running-set splice |83| MTP commit | Single-CAS reduction (consensus ∞) | Wait-free | Successful `committed_len` CAS |84| KV-block reclamation | Pass-the-Buck lineage → EBR | Lock-free (reclaimer) | Epoch-tick store |85| Request ring | Michael-Scott queue *(NOT Herlihy)* | Lock-free | fetch-add slot index |86| On-device sampling | Linearizability discipline | Wait-free | Single argmax CAS/store |8788## The honest hot-path caveat (read before you build)8990Two Herlihy constructions are **correctness references, not hot-path kernels**:9192- **The universal construction** (announce + consensus + help) proves any93 sequential object *can* be made wait-free — but its O(n) announce-scan per op94 serializes throughput. Borrow the **helping idea** and the **proof**, not the95 literal construction. Ship a bespoke single-CAS / fetch-add structure.96- **Transactional memory** gives multi-word atomicity but pays abort/retry and97 has no GPU-decode implementation. Reduce to one word instead.9899Where a hand-rolled lock-free structure already gives the needed progress (a100Treiber stack for the free-list, an MS-queue for the ring — both *not* Herlihy,101see `corpus.md`), use it. The doctrine tells you which primitive is *sound* and102which guarantee you *actually* have — it is not a mandate to build the general103construction literally.104105## Audit rubric (labeling an existing path's true guarantee)106107Cheap, high-value — run it before claiming any path is "lock-free":108109- **Single CAS / single release-store, bounded retries** → genuinely wait-free.110- **CAS-loop that can livelock under contention** → only lock-free (or111 obstruction-free) — do not call it wait-free.112- **Any Mutex / RwLock / epoch barrier / blocking wait on the path** → it is113 *blocking*, not non-blocking. That is the constraint to rewrite.114115Over-claiming a progress guarantee is the concurrency equivalent of an unscoped116"world-first" — the same honesty discipline applies. Label the true class, then117upgrade only the paths a token actually waits on.118119## The one-sentence version120121Name what needs agreement, pick CAS if it does and fetch-add if it doesn't,122give the commit one visible instant, claim only the progress you can prove, free123memory by epoch, and never chase a wait-free path topology already ruled out.