Skip to main content

StackOps

Trait StackOps 

Source
pub trait StackOps<const INDEX_BITS: u32>: StackStorage<INDEX_BITS> {
    // Required methods
    unsafe fn push_index(&self, index: u32) -> Result<(), TagExhausted>;
    fn pop_index(&self) -> Option<u32>;
}
Expand description

The stack operations — push_index / pop_index — blanket-implemented by the crate for every StackStorage implementor. Downstream impls are impossible (trait coherence: a second impl would conflict with this blanket), so the CAS-retry-loop bodies cannot be overridden or drifted from; an implementor controls only head/load_next/store_next.

Required Methods§

Source

unsafe fn push_index(&self, index: u32) -> Result<(), TagExhausted>

Push index onto the stack (classic Treiber push with a tag bump). The current head index (or TAIL when empty) is stored in index’s link with Release, then the head is CASed to (index, tag + 1). The numeric guard below is unconditional because an invalid index would corrupt the free-list and could double-issue a slot.

§Safety

The caller must uphold all three clauses:

  1. index is in this implementor’s documented link domain: it has a dedicated cell and the hooks are memory-safe for it. The runtime index < INDEX_MASK guard checks only the packed-word range, not this possibly narrower domain.
  2. index is not reachable through any binding whose hooks touch the same link cells. It was never pushed, or its latest push was followed by a successful pop_index returning it from any such binding. A stale popper that observed the index but lost its CAS did not pop it and does not block this push; the tag makes that stale CAS fail, and this push overwrites the old link before publishing the index through the receiving binding.
  3. This call owns a unique, unconsumed publish/recycle authority: either a fresh index or one legitimately transferred by a specific successful pop, including a pop through another binding sharing the cells. The authority is consumed at this call’s successful head CAS, not at physical return, so a later popper may republish the index with its own authority before this call returns. Two pushes may not consume the same authority without an intervening successful pop. These liveness and authority obligations are not runtime-checked; violating them can create a cycle or double-issue an index.
§Errors

Returns Err(TagExhausted) without publishing when the current tag is TaggedIndex::TAG_MAX. The head is then permanently sealed; the refused index remains the caller’s. A retry may have left stale link contents, which the next successful push overwrites before publishing.

§Panics

Panics if index >= INDEX_MASK (the empty sentinel is reserved), in both debug and release. The implementor’s link hooks may enforce a narrower bound; callers must satisfy that bound too.

Source

fn pop_index(&self) -> Option<u32>

Pop the top index off the stack, or None if empty. Loads the tagged head, reads its next link, and CASes the head to that link without changing the tag. A popper that loses its CAS retries with an Acquire observation; the monotonic, sealing tag prevents ABA.

When the popped element is the last one (next == TAIL), the empty sentinel keeps the observed running tag rather than resetting to zero. On a lost CAS whose actual head is already empty, retry backoff is skipped because the next iteration returns None (see the configured backoff cap).

load_next is reached through this implementor’s binding, whose head is read once for the whole CAS loop; see StackStorage.

§Panics

Panics if load_next returns neither TAIL nor an index below INDEX_MASK, or returns the popped index itself. The release-active guard prevents pack_truncating from turning an invalid value into a wrong live index or the empty sentinel. A self-loop indicates a caller contract violation; the guard is a detector, not a repair. Dedicated link storage is required because a stale popper may read a link after another thread has popped the index, and payload-aliasing could produce an arbitrary in-range value that passes this guard silently.

The implementor’s load_next may also panic on its own narrower domain bound; see StackOps::push_index’s # Panics.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl<const B: u32, S: StackStorage<B> + ?Sized> StackOps<B> for S