Skip to main content

StackStorage

Trait StackStorage 

Source
pub unsafe trait StackStorage<const INDEX_BITS: u32> {
    // Required methods
    unsafe fn head(&self) -> &StackHead<INDEX_BITS>;
    unsafe fn load_next(&self, index: u32) -> u32;
    unsafe fn store_next(&self, index: u32, next: u32);
}
Expand description

One implementor supplies a StackHead and atomic per-index links. The head↔links binding is fixed by the implementor; links may be slot-resident or fused in ArrayIndexStack. The implementation must be non-blocking if the resulting StackOps operations are to remain lock-free.

§Safety

Implementing this trait is a soundness commitment. The implementor must:

  1. Bind each head to exactly one live implementor and one link backing for its whole life; never share or rebind it while an index is reachable.
  2. Use one stable index↔cell mapping. After an Acquire observation of a head published by a Release push, load_next must observe that push’s store_next or a later write in the cell’s modification order, never an earlier write. A link cell may be mutated only by the stack-algorithm push that is about to publish its index through the binding currently receiving a valid, unique publish/recycle authority. That authority may be freshly issued or legitimately transferred by a successful pop from another binding sharing the cells. Later legitimate pop+repush operations may therefore write the same cell again. Out-of-band storage-owner, payload, direct, or forged writes are forbidden, even when they leave an acyclic, in-range chain.
  3. Keep reachable index populations disjoint across bindings sharing link cells. Sharing cells with disjoint populations is allowed, and a popped index may be transferred from one binding to another before the receiving binding publishes it.
  4. Return only TAIL or a valid index from a dedicated, non-payload- aliased link cell.
  5. Return the same logical head from head on every call.
  6. Document a fixed link domain, a subset of 0 .. INDEX_MASK, with a dedicated cell for every member. Hooks must be memory-safe in that domain; unchecked access outside it is permitted only because the caller contract guarantees the algorithm never supplies such an index.
  7. Make every link-cell access atomic; a stale popper may read while a concurrent push writes and then lose its head CAS.

§Shared-storage hazard class: detection boundary

The inventory counts binding relationships, not storage values. Shared cells are valid when reachable populations stay disjoint; a successful pop may transfer an index’s authority to another binding, which may then publish it. The forbidden shapes are:

ShapeForbidden arrangementDetector
1One binding reads and writes different backings.May eventually trip the self-loop guard.
2Two live bindings share one head but use different backings.May trip the self-loop guard; not structural.
3Bindings share cells while an index is reachable from both.Can remain acyclic and silently double-issue.
4A live head is rebound over time to a different backing.Can leak first, then trip the self-loop guard.

These are implementor obligations, not a complete runtime detector; direct forged writes and deeper acyclic corruption can pass every guard.

§Ordering contract

load_next must use Acquire (or stronger), and store_next must use Release (or stronger). Head publication already carries the link’s visibility, but these link orderings are deliberate defence-in-depth and keep an implementation independent of the stack’s internal head orderings; see docs/perf/TIS_LINK_ORDERING_WEAK_CAS_GATE.md for the measured status.

The three hooks are unsafe fn: the compiler requires an unsafe call site. Their caller-side contracts are stated on the methods below; the implementation obligations above remain the unsafe impl’s responsibility. The complete design rationale is in docs/adr/2026-09-01-tagged-index-stack-storage-binding-closure.md.

§Stability

This trait is intentionally open for external slot-resident implementations; future methods will have default bodies or require a major release.

Required Methods§

Source

unsafe fn head(&self) -> &StackHead<INDEX_BITS>

The stack’s head word.

§Safety

The caller may use the returned reference only as this binding’s head and must not create a competing head↔links binding around it.

Source

unsafe fn load_next(&self, index: u32) -> u32

Load index’s next link with Acquire ordering.

§Safety

index must have been pushed through this exact binding at least once, so its link cell was initialized by store_next. It need not remain reachable: a concurrent popper may win before this caller’s CAS.

Source

unsafe fn store_next(&self, index: u32, next: u32)

Store index’s next link with Release ordering. This is the only stack write to link storage, and it is lazy: links are written only during a push immediately before that push’s publishing CAS.

§Safety

The caller must be in the CAS-valid push phase: index satisfies StackOps::push_index’s three caller obligations, including a unique authority legitimately transferred from any binding sharing the cells; next is TAIL or the index observed as this binding’s head; and this call is made by the stack algorithm immediately before the CAS that publishes index. No storage-owner, payload, direct, or forged write may substitute for this call.

Dyn Compatibility§

This trait is dyn compatible.

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

Implementors§