internal/path_c_wrap¶
Internal module
lockfree/internal/path_c_wrap is an internal module. Its
API is not part of the umbrella's semver contract and may change
at any time. Downstream code MUST NOT import it directly.
Documented here for contributors and reviewers.
path_c_wrap implements the unbounded MPMC "path C — wrap" branch
of the consumer-side close protocol: the case where a closed cell is
observed on segment wrap-around and the consumer must advance to the
next segment without consuming a payload. See the LCRQ paper §4 for
the broader protocol context.
See also¶
path_c_admit— sibling close-CAS branch.- Queue — the body that dispatches into these paths.
path_c_wrap
¶
Path-C wrap/unwrap helpers.
Encode user-facing T into its SlotEncoding(T) slot form at push
time; decode back at pop time. Identity for POD T. Internal-only.
Lifecycle model:
library inc paired with library dec WITHIN library scopes. For
ref X the push wrapper does an explicit incRefSlot so the
queue claims +1 of the cell's refcount lifetime; on the caller side
the local binding's scope-exit =destroy balances back to net +1
owned by the slot. Pop is a destructive read via move — the
queue relinquishes the bits without a library decRefSlot (the
caller's binding inherits the queue's +1). The queue-side library
dec is the destroy-walk: disposeSlotEncoded runs decRefSlot
on every UNPOPPED slot, releasing the +1 the slot claimed at push.
Type dispatch (mirrors SlotEncoding in slot_encoding.nim):
* ref X → ManagedRef[X] via toManagedRef / toRef
* string → ManagedSlice[char] via wrap / unwrap
* seq[U] → ManagedSlice[U] via wrap / unwrapSeq
* else (POD) → T identity (assumed sizeof(T) <= sizeof(uint))
wrapOrIdentity ¶
template wrapOrIdentity(item: sink T): auto
Encode a user-facing T into its SlotEncoding(T) form.
ref X— bit-cast the pointer toManagedRef[X]AND callincRefSlotso the queue claims +1 of the cell's refcount lifetime. The caller'ssinkconsumption fires=destroyon the original binding when its scope ends, balancing back to net +1 owned by the slot. The destroy-walk (disposeSlotEncoded→decRefSlot) releases the +1 on any UNPOPPED slot; pop transfers the +1 to the caller's binding via destructivemove(no library dec at pop).string/seq[U]— box transfer viamanaged_slice.wrap; the queue holds the box pointer anddisposeSlotfrees the box on destroy-walk.- POD — identity;
sinkconsumes the source binding.
Parameters
-
item(sink T)
Returns
auto
unwrapOrIdentity ¶
template unwrapOrIdentity(encoded: SlotEncoding(T)): T
Decode a SlotEncoding(T) slot value back to user-facing T.
Pointer-bit / box-pointer transfer only — NO library refcount
touch at pop. Pop is a destructive read (move on the slot);
the queue's +1 refcount share (claimed by wrapOrIdentity at
push) is INHERITED by the caller's binding. =destroy will
fire on the caller's binding when their local leaves scope.
Parameters
-
encoded(SlotEncoding(T))
Returns
T
disposeSlotEncoded ¶
template disposeSlotEncoded(encoded: SlotEncoding(T))
Per-slot destroy-walk for an UNPOPPED slot. Reconstructs the
value and runs =destroy. Internal-only. Used by queue / bqueue
destructors when walking abandoned items.
- POD
T— identity. The encoded value is bit-for-bitT; no managed resources, no-op. ref X— delegate tomanaged_ref.decRefSlotwhich drops the cell's refcount via the per-MM shim (arc/orc/atomicArc/refc →GC_unref; none → no-op; nimony →arcDec). This is the analogue ofmanaged_slice.disposeSlotfor the ref-T arm. We intentionally do NOT use the "reconstruct a localref Xand let it leave scope" pattern: under--mm:arcthe compiler's cursor inference treats such a local as a non-owning borrow and elides=destroy, leaking the refcount.decRefSlotcallsGC_unrefon the bit-castref Xdirectly, which is immune to cursor elision.string— delegate tomanaged_slice.disposeSlot(non-generic StringBox path) which destroys the box payload and frees the box.seq[U]— delegate tomanaged_slice.disposeSeqSlot(distinctly-named SeqBox path). MUST NOT calldisposeSlot: forseq[char]the slot encoding isManagedSlice[char], which would resolve to the non-generic stringdisposeSlotand run the StringBox destructor over a SeqBox.
All arms tolerate the zero / nil-bits sentinel: decRefSlot
short-circuits on nil bits via its own guard;
managed_slice.disposeSlot / disposeSeqSlot check the box
pointer for nil.
Parameters
-
encoded(SlotEncoding(T))