ManagedRef¶
ManagedRef[T] is a single-owner, move-tracked reference to a
heap-allocated T, owned by an underlying reclamation scheme.
Unlike ref T (GC-managed) or ptr T (raw, unmanaged), ManagedRef
carries an explicit ownership/lifetime contract that integrates with
the umbrella's SMR (nebr) and with the queue payload protocols.
Construction allocates; moves transfer ownership; destruction either
retires through the manager (when alive) or is a no-op (when moved
out / sunk).
See also¶
- ManagedSlice — managed-payload analog for contiguous arrays.
- Chronos — managed temporal payload.
- SMR / nebr — the reclamation backend used by the managed payload types.
managed_ref
¶
lockfree/managed_ref
Internal slot encoding for ref T payloads under Path C
(Queue[ref Foo, ...]). ManagedRef[X] is the internal type with
per-MM refcount shim arms and bit-cast guarantees.
NEVER user-facing¶
ManagedRef[X] is the wire-format the queue stores in its slot
array. The user-facing API is ref T. ManagedRef MUST NOT
leak into public docs, examples, or signatures.
Why distinct uint and not distinct ptr X¶
Atomic[uint]andAtomic[Pair[uint, uint]]compile and produce the rightcmpxchg16b/ldxp+stxpinstructions across every atomics backend we target.Atomic[ptr X]is less well-trodden and the strict-LCRQ DWCAS arm cannot tolerate "may or may not compile".distinct uintopacifies the slot at the compiler's lifecycle pass — no implicit=destroy/=copyis emitted on the slot value because the compiler sees POD bits. Refcount operations happen at the EXPLICIT call sites in the queue wrappers (push, pop, queue-destroy walk), never silently behind the queue's back.cast[uint](myRef) ↔ cast[ManagedRef[X]](bits) ↔ cast[ref X](toBits(mref))is a no-op at runtime and preserves pointer identity, which is the precondition for the per-MM refcount calls to find the correct heap header.
ABI stability¶
sizeof(ManagedRef[X]) and alignof(ManagedRef[X]) are equal
to sizeof(uint) and alignof(uint) on every supported
platform. This is the wire-format invariant the queue's atomic
slot operations depend on, and it is asserted at compile time in
the static: block below. Across-MM ABI promises (arc ↔ orc ↔
atomicArc ↔ none) follow from this identity plus the same field
layout in Queue[T, ...]. What we explicitly do NOT promise:
refc bit-compat, cross-Nim-version, cross-minor-version.
Per-MM shim arms¶
The incRefSlot / decRefSlot templates dispatch on the
compile-time MM define:
arc/orc/atomicArc— bump/drop the cell's refcount via the same per-MM mechanism the compiler would emit for aref X. The atomicArc arm collapses with arc in thewhenbecause the C-RTL substitutes the atomic op transparently.refc— tracing GC:GC_ref/GC_unref.none— strict bit-transport contract: no-op. The user owns lifetime; the queue is pointer-bit transport only.nimony— usesarcInc/arcDecfrom nimony'sstd/system/arcops. The signature shape differs from Nim 2.x'snimIncReffamily: nimony'sarcInc(memLoc: var int)andarcDec(memLoc: var int): booloperate on the refcount field directly (avar intlvalue), NOT a heap-pointer. The arm below usescast[ptr int](bits)[]to materialise avar intlvalue at the bits location. See partial-port boundary block below for heap-header offset and dispose-symbol caveats.
Implementation note: nimIncRef / nimDecRefIsLast /
nimDestroyAndDispose are the underlying symbol path. Those are
compilerRtl and emitted static in their TU, so a
cross-module call site requires a thin wrapper. GC_ref /
GC_unref are the exported wrappers around that exact path —
under arc/orc/atomicArc they delegate to nimIncRef /
=destroy (which in turn calls nimDecRefIsLast and
nimDestroyAndDispose); under refc they hit the tracing-GC path.
The semantic is identical; the symbol path differs only in being
public. The bit-cast guarantee makes either choice equivalent.
ManagedRef
¶
type ManagedRef[X] = distinct uint
Slot encoding for a ref X payload. Internal — see module
doc-comment. Sized and aligned identically to uint.
toManagedRef inline ¶
proc toManagedRef(r: sink ref X): ManagedRef[X]
Pack a ref X into the slot encoding. Pointer-bit transfer
only — does NOT touch refcount. The queue wrapper
(path_c_wrap.nim:wrapOrIdentity) is responsible for the
paired incRefSlot that claims +1 of the cell's refcount
lifetime for the slot. See path_c_wrap.nim.
Parameters
-
r(sink ref X)
Returns
ManagedRef[X]
toRef inline ¶
proc toRef(mref: ManagedRef[X]): ref X
Unpack the slot encoding back to ref X. Pointer-bit transfer
only — does NOT touch refcount. Pop is destructive (move);
the queue's +1 refcount share (claimed at push) is inherited by
the caller's binding. See path_c_wrap.nim.
Parameters
-
mref(ManagedRef[X])
Returns
ref X
toBits inline ¶
proc toBits(mref: ManagedRef[X]): uint
Expose the raw pointer bits. Used by the queue's atomic slot
operations (Atomic[uint] ops on the slot array). Identity
codegen.
Parameters
-
mref(ManagedRef[X])
Returns
uint
fromBits inline ¶
proc fromBits(_: typedesc[ManagedRef[X]]; bits: uint): ManagedRef[X]
Reconstruct a ManagedRef[X] from raw bits read out of the
slot array. Identity codegen.
Parameters
-
_(typedesc[ManagedRef[X]]) -
bits(uint)
Returns
ManagedRef[X]
nilManagedRef inline ¶
proc nilManagedRef(_: typedesc[X]): ManagedRef[X]
The all-zero slot — semantically equivalent to a nil ref X.
A generic proc rather than a const because the generic
ManagedRef[X] cannot be a top-level const without
binding X. Call as nilManagedRef(Foo) (typedesc passed
explicitly) from generic queue code where X is bound by
context.
Parameters
-
_(typedesc[X])
Returns
ManagedRef[X]
incRefSlot ¶
template incRefSlot(mref: ManagedRef[X])
Bump the refcount of the cell pointed at by mref. No-op when
mref is the nil slot. Per-MM dispatch.
Parameters
-
mref(ManagedRef[X])
decRefSlot ¶
template decRefSlot(mref: ManagedRef[X])
Drop the refcount of the cell pointed at by mref. No-op when
mref is the nil slot. Per-MM dispatch.
Parameters
-
mref(ManagedRef[X])
incRefSlot ¶
template incRefSlot(mref: ManagedRef[X])
Nimony arm of incRefSlot. Calls arcInc(memLoc: var int) on
the cell's refcount. Experimental.
Partial-port note: the rc field is assumed to live AT the slot bits address. The verified nimony heap-header offset is tracked at v0.2 — see module-level partial-port block.
Parameters
-
mref(ManagedRef[X])
decRefSlot ¶
template decRefSlot(mref: ManagedRef[X])
Nimony arm of decRefSlot. Calls arcDec(memLoc: var int): bool
on the cell's refcount. Experimental.
Partial-port note: the dispose-on-last-ref symbol is omitted pending resolution. See module-level partial-port block.
Parameters
-
mref(ManagedRef[X])
reset inline ¶
proc reset(mref: var ManagedRef[X])
Zero the slot AND drop the refcount in one balanced operation.
Used by the queue's destructor walk and by push-failure
rollback. No-op when mref is already nil.
Parameters
-
mref(var ManagedRef[X])