pub struct StackHead<const INDEX_BITS: u32> { /* private fields */ }Expand description
The head word of a tagged Treiber free-list: a single AtomicU64 packing an
(index | tag) pair (see TaggedIndex). Owned by exactly one
StackStorage implementor value at a time, and bound to one link
backing for its WHOLE life — the binding between this head and its links
is established by that impl, not re-asserted per call; sharing one head
between implementor values (clause 1) or rebinding a live head across time
are hazards — see the StackStorage trait’s # Safety contract. The
stack operations themselves live
on StackOps (blanket-implemented by the crate), not here; this type
is the bare atomic embedders inherit a cache line through.
§Layout note — no cache-line isolation
This type is a bare AtomicU64 with no padding or alignment of its own —
#[repr(transparent)] makes that a compiler-enforced guarantee (layout,
size, and ABI identical to the single head field), not an incidental
property of the current definition — so it inherits the cache line of
whatever struct embeds it. If it lands adjacent
to another frequently-modified atomic, the two fields false-share — each
write invalidates the other core’s copy of the line, and contending cores
ping-pong the line even though the atomics are logically independent. That
costs throughput, never correctness, and only matters when the line is
genuinely hot. Fix it at the embedding site when a profile shows it — wrap
this stack in a #[repr(align(64))] newtype or interpose padding — rather
than paying for blanket alignment inside the crate, which would waste most
of a cache line for every embedder that does not need the isolation.
§Sealing is permanent — no reset
Once pushes_remaining reaches 0 (the tag is
TaggedIndex::TAG_MAX), this head is sealed and stays sealed: there is
no reset/rotation API, and none will be added. An in-place reset would be
a plain store on head — breaking the release-sequence invariant
documented on the private field below — AND would restore tag 0,
reintroducing the exact full-wrap collision this seal exists to close
(see the crate-root docs’ tag-preservation note on why a naive tag reset is unsound in
general). A sealed head cannot be reset. A replacement must be a
distinct StackHead object; if it reuses the same link cells and index
population, the sealed head must first be fully drained
(pop_index → None repeatedly until empty) and
must outlive every popper that may still reference it — for a 'static
head, forever.
Implementations§
Source§impl<const INDEX_BITS: u32> StackHead<INDEX_BITS>
impl<const INDEX_BITS: u32> StackHead<INDEX_BITS>
Sourcepub const fn new() -> Self
pub const fn new() -> Self
A fresh, EMPTY stack head (the bootstrap empty sentinel, tag 0). Under
--cfg loom this cannot be const (loom’s atomics have no const ctor).
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Whether the stack is currently empty. Advisory only — a concurrent
push or pop can make the answer stale the instant this returns, in
either direction — so use it for diagnostics/monitoring, not for
correctness decisions (pop_index’s None is
the authoritative empty check).
A Relaxed load is sufficient here because the result is explicitly
racy: no ordering is being promised, and a plain load touches nothing,
so the release-sequence invariant documented on the private head
field is untouched.
Sourcepub fn pushes_remaining(&self) -> u64
pub fn pushes_remaining(&self) -> u64
Successful pushes this head can still accept before
push_index starts refusing with
Err(TagExhausted): TaggedIndex::TAG_MAX - tag. 0 means
sealed — every future push is refused (see “Sealing is permanent”
above).
Advisory Relaxed load, same posture as is_empty:
a concurrent push can make this stale the instant it returns. A
plain load touches nothing, so the release-sequence invariant on the
private head field (see its doc) is untouched.