Every push into an SPSC ring must answer one question: is there room? A naive producer reloads the consumer's cursor on every push, even when the answer has not changed.
Astral keeps a local copy instead. The producer owns the head cursor and caches the consumer's tail; the consumer owns the tail and caches the producer's head. Because both cursors only move forward, an old copy can hide newly available room or work, but it cannot invent either one.
This article explains why that staleness is safe, shows the remote load behind the boundary branch, and scopes the sub-nanosecond measurements to the isolated cursor probe that produced them.
The local fast path
// standalone illustration, clang++ -O2 -std=c++17 (x86-64), not the astral source
bool spsc_push(SpscRing& r, void* item) {
uint64_t h = r.head.load(std::memory_order_relaxed);
uint64_t next = h + 1;
if (next - r.cached_tail > r.capacity) { // check the local copy first
r.cached_tail = r.tail.load(std::memory_order_acquire); // refresh only at the boundary
if (next - r.cached_tail > r.capacity)
return false; // genuinely full
}
r.slots[h & (r.capacity - 1)] = item;
r.head.store(next, std::memory_order_release); // publish
return true;
}
The standalone push loads the producer-owned head, compares next against the local cached_tail, and jumps straight to publication when the cached view proves there is room. The acquire load of the consumer-owned tail executes only when that first check fails.
; clang++ 18 -O2 (x86-64), standalone spsc_push
mov rcx, qword ptr [rdi] ; h = producer-owned head
lea rax, [rcx + 1] ; next = h + 1
mov r8, rax
sub r8, qword ptr [rdi + 8] ; next - cached_tail
mov rdx, qword ptr [rdi + 88] ; capacity
cmp r8, rdx
jbe .push ; cached view proves room
mov rdx, qword ptr [rdi + 64] ; acquire-load remote tail
mov qword ptr [rdi + 8], rdx ; refresh cached_tail
; subtract and compare again; return false if still full
.push:
mov r8, qword ptr [rdi + 80] ; slots base
dec rdx ; capacity - 1
and rdx, rcx ; h & (capacity - 1)
mov qword ptr [r8 + 8*rdx], rsi ; publish payload
mov qword ptr [rdi], rax ; head.store(next, release)
ret
The isolated cached-cursor probe measured 0.48 ns per single-item check and 0.32 ns per item in the batched case. These are local bookkeeping costs, not end-to-end transit latency and not the full production push.
I checked the probe's disassembly because sub-nanosecond numbers are easy to misread. Its local path is ordinary loads, arithmetic, a compare, and publication, with no lock-prefixed instruction. A lock addl elsewhere in the binary belongs to harness bookkeeping, so the review had to inspect the surrounding symbol instead of counting lock prefixes.
Astral's production ring adds transition-sensitive wake logic around this cursor protocol. It signals when a push changes empty to nonempty and when a pop changes full to not full. On ARM, the transition check refreshes the remote cursor before deciding whether to signal, so a stale cached value cannot suppress the wake.
The same physics shows up wherever two cores write near each other, and the cache-line layout rules it obeys have a post of their own. What makes this ring's version of the trick safe is the direction of the error: cursors only move forward, so a stale copy can understate available space or items, never overstate them. The failure mode is an unnecessary refresh, not a correctness bug.