A correct counter update does not imply that the related object is still alive; a snapshot retry does not mean that a bad access can be undone. Synchronization must address mutual exclusion, ordering, and lifetime simultaneously.
Objective
Section
Locks and Context
Sections 1–4
Consistent Snapshots and Atomic Operations
Sections 5–6
Publishing, Waiting, and Exit
Sections 7–8
The goal of synchronization is not merely to prevent two CPUs from writing to the same address simultaneously. It also includes maintaining multi-field invariants, restricting object lifetimes, ensuring data publication order, and avoiding interrupt reentrancy on the same CPU. Before choosing a lock, list all readers/writers and their contexts.
For example, a queue consists of head, tail, and count. Changing count to atomic does not protect node links; a reader might see a new node before count is updated, or continue accessing a node after it has been freed. First, clearly define "when the queue is visible to readers, who can delete, and who is responsible for freeing."
1. Choose Based on Constraints, Not Arbitrary Weightings
Requirement
Common Choice
Conditions Not to Ignore
Protecting compound state between sleepable tasks
mutex
Must be released by the same owner; does not support recursion
Short critical sections in truly atomic/interrupt contexts
Appropriate spinlock / raw_spinlock
Verify RT semantics and interrupt reentrancy
Multiple readers needing stable shared state
rw_semaphore
Blocking is allowed while held, which may delay writers
Re-readable snapshots of few scalars
seqcount / seqlock
Writers are serialized; readers can retry; no pointer lifetime vulnerabilities
Low-overhead read access to replaceable objects
RCU
Handle writer mutual exclusion and deferred reclamation separately
Independent atomic state or counters
atomic / refcount, etc.
Atomicity, ordering, and object lifetime must be designed separately
Waiting for an operation to complete or a condition to change
completion / wait queue
These do not replace locks protecting shared data
"Read-heavy, write-light" is merely a statistical characteristic and does not automatically dictate the use of RCU or seqlock. Whether readers must see the latest value, whether they can sleep, whether retries are allowed, and whether objects must be held across critical sections all influence the choice.
2. What Spinlocks and Local Masking Do Separately
In non-PREEMPT_RT kernels, spin_lock() itself disables preemption to maintain the semantics of the corresponding critical section; it does not require the caller to disable preemption beforehand. However, it does not automatically mask local hardirqs.
Assume a task holds lock L, and subsequently, the interrupt handler on the same CPU also acquires L: the handler spins waiting for the task to release it, but the task cannot run before the handler returns, forming a self-deadlock loop. Use protocols like irqsave on the appropriate access side to avoid such reentrancy.
spin_lock_irqsave() saves the local interrupt state, masks interrupts, and acquires the lock; irqrestore restores the original state.
_irq() / unlock_irq do not save the original state; the caller must satisfy their entry state conventions.
_bh() handles local bottom-half/softirq concurrency; this does not imply that hardirq or NMI are also masked.
Disabling local interrupts does not prevent other CPUs from accessing shared data; true cross-CPU mutual exclusion comes from locks. NMI is not controlled by standard interrupt masking; data used in NMI must be designed separately.
PREEMPT_RT Changes the Implementation and Semantics of spinlock_t
In RT kernels, the standard spinlock_t is based on a blocking real-time locking mechanism; do not assume preemption is disabled after acquisition. Its hardirq suffix is also not equivalent to local hard interrupt masking on non-RT systems. raw_spinlock_t retains strict spinning and corresponding atomic semantics, used in low-level scenarios where it is truly needed.
This is not an encouragement to replace all locks with raw spinlocks: excessively long non-preemptible sections harm real-time performance, and you cannot call the standard RT spinlock acquisition path (which may block) while holding a raw lock. See the Lock Types Documentation for type and nesting rules.
3. Contention Paths in qspinlock and Mutex
qspinlock combines compact lock state with queued waiting, including uncontended, pending, and queue paths; contenders can use MCS-style nodes to reduce the overhead of all CPUs constantly contending for the same lock word. It is not the case that "all contenders only ever touch their own cache line"; unlocking, head/tail handoffs still require shared state.
The implementation's bit layout is also configuration-dependent; read include/asm-generic/qspinlock_types.h. Do not arbitrarily draw locked/pending/tail as fixed ABIs by byte. Architectures and virtualization may also use different implementations.
The uncontended path of a mutex quickly acquires ownership; under contention, it can optimistically spin or sleep waiting, depending on conditions. Waking a waiter does not guarantee it runs on the CPU immediately; fairness, handoff, and owner state handling cannot be abbreviated to a single owner=0.
Holding a mutex allows sleeping in allowed contexts, but consider whether the sleep path requires the same lock; interfaces like mutex_lock_interruptible() can return without acquiring the lock, so the caller must check. A standard mutex does not equal an rt_mutex providing priority inheritance. See Mutex Design.
4. rwsem and percpu_rwsem Are Different Interfaces
rw_semaphore allows multiple readers or one writer; percpu_rw_semaphore is a specialized interface that leverages per-CPU read-side state and corresponding synchronization mechanisms to optimize certain read-heavy, write-light scenarios. It is not the case that a standard rwsem automatically becomes per-CPU in some version.
Read locks can still wait for write locks; a reader sleeping for a long time will block writers. The relationship between multiple readers and priority inheritance under RT requires special attention; do not promise strict wait-time upper bounds in arbitrary configurations.
If you attempt to upgrade directly from a read lock to a write lock, you may wait mutually with other upgraders. Release and re-check conditions according to interface support, acquire the write lock from the start, or design a specific conversion protocol.
5. Seqlock: Getting a Consistent Snapshot at the Cost of Potential Retries
When a writer enters, it advances the sequence to the writing state; when it exits, it advances it again; the reader copies data and then checks if the sequence has changed. write_seqlock() / unlock manage the sequence; callers must not manually seq++ between them.
Below is an example of a kernel C translation unit that can be compiled independently, without module registration. It demonstrates the invariant high = low + 1 for two scalars; the read result is only used after a successful check.
#include<linux/compiler.h>#include<linux/seqlock.h>#include<linux/types.h>staticDEFINE_SEQLOCK(range_lock);static u32 range_low;static u32 range_high =1;voidkb_set_range(u32 low);voidkb_get_range(u32 *low, u32 *high);voidkb_set_range(u32 low){write_seqlock(&range_lock);WRITE_ONCE(range_low, low);WRITE_ONCE(range_high, low +1);/* u32 wraparound is allowed in this example */write_sequnlock(&range_lock);}voidkb_get_range(u32 *low, u32 *high){unsignedint seq;
u32 a, b;do{
seq =read_seqbegin(&range_lock);
a =READ_ONCE(range_low);
b =READ_ONCE(range_high);}while(read_seqretry(&range_lock, seq));*low = a;*high = b;}
Reader's Action
Stored Result
Can it be passed to the caller?
Read old low first
a = 0
No, the snapshot has not been verified
Writer updates to (10, 11), reader then reads high
(a, b) = (0, 11)
No, the combination breaks high = low + 1
Sequence check detects an update
Discard temporary combination and re-read
Only usable if the check succeeds
During the retry interval, do not perform irreversible external side effects based on temporary values.
This example assumes the access context matches standard seqlock; if interrupt readers can preempt writers, the writer also needs corresponding local masking, otherwise readers might wait for a writer that was preempted by themselves. Pure seqcount itself does not serialize multiple writers; there must be a separate write-side lock, satisfying rules such as non-preemptibility.
Read low=0, allow a writer to replace the pair with 10/11, then read high=11. Both loads can be valid individually while their combination is invalid. A retry must discard that combination, including any derived result. Do not perform irreversible external side effects before validation.
Preparing the visual
Can two collected values be used as one snapshot?
Temporary reads can be inconsistent. Use them only when the sequence is unchanged and even. Retrying protects snapshot consistency, not invalid pointer accesses or nonrepeatable side effects.
{"id":"seqlock-interleavings","title":"Can two collected values be used as one snapshot?","summary":"Temporary reads can be inconsistent. Use them only when the sequence is unchanged and even. Retrying protects snapshot consistency, not invalid pointer accesses or nonrepeatable side effects.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"two\"><div class=\"card\"><header data-i18n=\"writer\"></header><div class=\"actions\"><button id=\"beginWrite\" data-i18n=\"beginWrite\"></button></div><div class=\"actions\"><button id=\"writeLow\" data-i18n=\"writeLow\"></button></div><div class=\"actions\"><button id=\"writeHigh\" data-i18n=\"writeHigh\"></button></div><div class=\"actions\"><button id=\"endWrite\" data-i18n=\"endWrite\"></button></div></div><div class=\"card\"><header data-i18n=\"reader\"></header><div class=\"actions\"><button id=\"beginRead\" data-i18n=\"beginRead\"></button></div><div class=\"actions\"><button id=\"readLow\" data-i18n=\"readLow\"></button></div><div class=\"actions\"><button id=\"readHigh\" data-i18n=\"readHigh\"></button></div><div class=\"actions\"><button id=\"validate\" data-i18n=\"validate\"></button></div></div></div><div class=\"track-row\"><span data-i18n=\"shared\"></span><div id=\"shared\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"draft\"></span><div id=\"draft\" class=\"track\"></div></div><div class=\"card\"><header data-i18n=\"result\"></header><div id=\"result\" class=\"readout\"></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"note\"></p></div><div class=\"actions\"><button id=\"reset\" data-i18n=\"reset\"></button></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.card .actions{margin:8px 0}.card .actions button{min-height:5em}.readout{min-height:5.3em}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}let seq=0,low=0,high=1,writing=false,target=0,wrote=[false,false],reading=false,start=null,a=null,b=null,result='initial';function draw(){cells('shared',[{text:'low = '+low},{text:'high = '+high},{text:'seq = '+seq,cls:seq%2?'gold':'on'}]);cells('draft',[{text:'a = '+(a??'—')},{text:'b = '+(b??'—')},{text:'seq₀ = '+(start??'—'),cls:reading?'on':''}]);$('#beginWrite').disabled=writing;$('#writeLow').disabled=!writing||wrote[0];$('#writeHigh').disabled=!writing||wrote[1];$('#endWrite').disabled=!writing||!wrote.every(Boolean);$('#beginRead').disabled=reading||seq%2===1;$('#readLow').disabled=!reading;$('#readHigh').disabled=!reading;$('#validate').disabled=!reading||a===null||b===null;set('result',t(result)+(result==='initial'?'':'\\n('+a+', '+b+')'));set('explanation',t('note'));}$('#beginWrite').onclick=()=>{writing=true;seq++;target=low+10;wrote=[false,false];draw();};$('#writeLow').onclick=()=>{low=target;wrote[0]=true;draw();};$('#writeHigh').onclick=()=>{high=target+1;wrote[1]=true;draw();};$('#endWrite').onclick=()=>{writing=false;seq++;draw();};$('#beginRead').onclick=()=>{reading=true;start=seq;a=b=null;result='initial';draw();};$('#readLow').onclick=()=>{a=low;draw();};$('#readHigh').onclick=()=>{b=high;draw();};$('#validate').onclick=()=>{result=start===seq&&seq%2===0?'accepted':'retry';reading=false;draw();};$('#reset').onclick=()=>{seq=low=0;high=1;writing=reading=false;wrote=[false,false];start=a=b=null;result='initial';draw();};draw();","audio":false,"strings":{"accepted":"Accepted: snapshot usable","beginRead":"Begin / retry read","beginWrite":"Begin update","draft":"Draft values and starting sequence","endWrite":"End update","heading":"Can two collected values be used as one snapshot?","initial":"Not validated yet","intro":"Interleave writer and reader actions freely. Make low come from the old version and high from the new, then validate whether the sequence permits publishing that result.","note":"Try: begin read → read low → complete a writer update → read high → validate. The draft retains mixed values, but validation rejects them.","readHigh":"Read high","readLow":"Read low","reader":"Reader: temporary copy","reset":"Reset","result":"Last validation","retry":"Rejected: discard and retry","scope":"One serialized writer, two scalars with high=low+1. Sequence wrap is omitted. Reader begin is disabled while odd, illustrating read_seqbegin waiting for a stable sequence. Buttons expose internal accesses for observation and are not a copyable kernel implementation.","scopeLabel":"Model scope and assumptions","shared":"Shared values and current sequence","validate":"Validate snapshot","writeHigh":"Write high","writeLow":"Write low","writer":"Writer: shared fields"}}
Snapshot Consistency and Pointer Lifetime Are Guaranteed Separately
Retries cannot fix illegal accesses that have already occurred. If a reader accesses a freed object via a pointer in the snapshot, and then discovers the sequence has changed, it is still a UAF (Use-After-Free). Pointer lifetime must be protected by RCU, reference counting, or other mechanisms. The read side must not perform non-repeatable external side effects in regions where retries are possible. See seqcount/seqlock.
6. Atomic Operations, Refcounts, and Ordering
atomic_read/set, non-returning RMW, returning RMW, and acquire/release/relaxed variants have different ordering guarantees; do not call all atomic operations full barriers. Failed conditional operations also require separate checks of return values and ordering semantics.
Object references should prefer appropriate interfaces like refcount_t, leveraging overflow/underflow protection and specific lifetime semantics; general counters do not automatically prevent "resurrection from zero" or safe acquisition of objects that may already be freed.
CAS (Compare-And-Swap) can atomically compare and replace a state, but it does not automatically eliminate the ABA problem: if a value goes through A→B→A, the comparison can still succeed. Lock-free structures often also require versioning, reclamation protocols, and strict ordering design. See atomic types.
7. Publishing Data Requires Matching Observation Relationships
An example of publishing once and not modifying the payload afterwards: the writer fills the payload first, then calls smp_store_release(&ready, 1); the reader reads the payload only after observing the published value via smp_load_acquire(&ready).
The effect of release/acquire comes from matching publication and observation, not from any two CPUs simply executing a barrier once to automatically synchronize. If ready is reused or the payload is overwritten in a loop, additional protocols are needed to prevent competition between old readers and new writers.
Tool
Layer Constrained
READ_ONCE / WRITE_ONCE
Compiler behavior related to single accesses
barrier()
Compilation order
smp_* barriers
Corresponding cross-CPU memory ordering
Device accessors and dma_* barriers
Corresponding device access protocols for MMIO/DMA
These operations are not a unified "cache flush," and single-access constraints do not independently establish complete publication relationships. See the Memory Barriers Documentation.
READ_ONCE/WRITE_ONCE constrain particular accesses and compiler transformations; a compiler barrier is not a CPU barrier. smp barriers, DMA ordering, and MMIO accessors address different domains. None should casually be described as “flush all caches.”
Preparing the visual
Why does observing ready=1 still need a publication relation?
Only an acquire observation of the corresponding release value provides the matched ordering needed for the earlier payload write and later read. READ_ONCE/WRITE_ONCE alone do not establish that full relation.
{"id":"publication-ordering","title":"Why does observing ready=1 still need a publication relation?","summary":"Only an acquire observation of the corresponding release value provides the matched ordering needed for the earlier payload write and later read. READ_ONCE/WRITE_ONCE alone do not establish that full relation.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><label class=\"check\"><input type=\"checkbox\" id=\"release\"><span data-i18n=\"release\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"acquire\"><span data-i18n=\"acquire\"></span></label><div class=\"two\"><div class=\"card\"><header data-i18n=\"writer\"></header><div class=\"event\">payload = 1</div><div id=\"write-edge\" class=\"order-edge\">↓</div><div id=\"store\" class=\"event\"></div></div><div class=\"card\"><header data-i18n=\"reader\"></header><div id=\"load\" class=\"event\"></div><div id=\"read-edge\" class=\"order-edge\">↓</div><div class=\"event\">read payload</div></div></div><div class=\"rf\" data-i18n=\"observes\"></div><p class=\"caption\" data-i18n=\"choose\"></p><div class=\"outcomes\"><div id=\"candidate-bad\"><code data-i18n=\"bad\"></code><strong id=\"verdict-bad\"></strong></div><div id=\"candidate-good\"><code data-i18n=\"good\"></code><strong id=\"verdict-good\"></strong></div></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"unmatched\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"matched\"></p></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.event{border:1px solid var(--rule);border-radius:5px;min-height:5em;padding:9px 5px;display:flex;align-items:center;font:12px var(--viz-font-ui);overflow-wrap:anywhere}.order-edge{text-align:center;font-size:28px;color:var(--rule)}.order-edge.on{color:var(--accent)}.rf{border-block:1px dashed var(--accent);padding:12px 4px;font-size:12px;text-align:center}.outcomes>div{display:flex;justify-content:space-between;gap:10px;padding:16px 0;border-bottom:1px solid var(--rule);font-size:12px;min-height:5em}.outcomes code{overflow-wrap:anywhere}.outcomes strong{font-weight:500;color:var(--accent)}.outcomes .forbidden code{text-decoration:line-through;color:var(--muted)}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}function draw(){const r=$('#release').checked,a=$('#acquire').checked,matched=r&&a;set('store',r?'store_release(ready, 1)':'WRITE_ONCE(ready, 1)');set('load',a?'load_acquire(ready) = 1':'READ_ONCE(ready) = 1');$('#write-edge').classList.toggle('on',r);$('#read-edge').classList.toggle('on',a);$('#candidate-bad').classList.toggle('forbidden',matched);set('verdict-bad',t(matched?'forbidden':'allowed'));set('verdict-good',t('allowed'));set('explanation',t(matched?'matched':'unmatched'));}$('#release').onchange=$('#acquire').onchange=draw;draw();","audio":false,"strings":{"acquire":"Reader: load_acquire","allowed":"Allowed","bad":"ready=1, payload=0","choose":"Compare two candidate outcomes","forbidden":"Forbidden","good":"ready=1, payload=1","heading":"Why does observing ready=1 still need a publication relation?","intro":"Enable writer release and reader acquire independently. Inspect ordering edges and whether “ready=1, payload=0” remains an allowed outcome.","matched":"The release matches the acquire that observes it. The chain is complete, excluding the payload=0 candidate.","observes":"The ready read observes this store (reads-from)","reader":"Reader","release":"Writer: store_release","reset":"Reset","scope":"One-time publication, initially payload=ready=0, with no later payload writes. The reader is fixed to observe ready=1. Outcomes describe a generic weak LKMM setting, not their frequency on a specific CPU; other synchronization is absent. Edges express ordering, not a cache-flush animation.","scopeLabel":"Model scope and assumptions","unmatched":"Without the matched relation, old payload cannot be excluded. Enabling just one side does not complete the ordering proof.","writer":"Writer"}}
8. Waiting Must Cooperate with Conditions and Lifetimes
Wait queues use interfaces like wait_event to register waits and check conditions under the correct protocol; the waker updates the condition first, then notifies. Simply writing if (!ready) schedule() will lose notifications between the check and the sleep.
completion is suitable for one-time or controlled repeated completion events; a timeout return does not cancel asynchronous operations; the wait object and callback context must remain valid. When destroying work, first prevent new submissions, then synchronously cancel/wait, and finally free resources. Do not hold locks required by the completion path during waiting.