Skip to content

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 to ManagedRef[X] AND call incRefSlot so the queue claims +1 of the cell's refcount lifetime. The caller's sink consumption fires =destroy on 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 destructive move (no library dec at pop).
  • string / seq[U] — box transfer via managed_slice.wrap; the queue holds the box pointer and disposeSlot frees the box on destroy-walk.
  • POD — identity; sink consumes 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-bit T; no managed resources, no-op.
  • ref X — delegate to managed_ref.decRefSlot which 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 of managed_slice.disposeSlot for the ref-T arm. We intentionally do NOT use the "reconstruct a local ref X and let it leave scope" pattern: under --mm:arc the compiler's cursor inference treats such a local as a non-owning borrow and elides =destroy, leaking the refcount. decRefSlot calls GC_unref on the bit-cast ref X directly, which is immune to cursor elision.
  • string — delegate to managed_slice.disposeSlot (non-generic StringBox path) which destroys the box payload and frees the box.
  • seq[U] — delegate to managed_slice.disposeSeqSlot (distinctly-named SeqBox path). MUST NOT call disposeSlot: for seq[char] the slot encoding is ManagedSlice[char], which would resolve to the non-generic string disposeSlot and 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))