Skip to content

atomics

lockfree/atomics is the umbrella's atomics facade. It provides the typed atomic primitives used internally by the queue bodies and the SMR layer: relaxed/acquire/release/seq-cst loads and stores, CAS, fetch-add, and the bounded back-off loop used on contention.

Downstream code generally does not need to import this directly — the queue and SMR APIs encapsulate the atomic operations. It is documented here for callers writing additional lock-free primitives against the same memory-ordering discipline used by the umbrella.

See also

  • lockfree/atomics/dsl — the macros used to declare atomic fields and load/store sites with explicit memory ordering.
  • lockfree/atomics/backoff — the spin/yield/back-off loop used by contention paths.

atomics

debra/atomics

Custom atomics for nebr (and lockfree).

Goals over std/atomics: * Reject Atomic[ref T] at compile time (no silent spinlock). * Require Atomic[T] to be lock-free at compile time. * Validate MemoryOrder per op at compile time. * Statically assert alignof(Atomic[T]) >= sizeof(T) so a target where T's natural alignment is insufficient (e.g. uint64 on some 32-bit ABIs) fails to compile rather than silently downgrading to a non-lock-free split-lock object. The assert is the safety boundary; we cannot force alignment higher than the type's natural alignment from generic code (see Atomic[T] doc), so we trap the mismatch instead.

Design doc: docs/design/2026-04-25-custom-atomics.md

User guide: see docs/guide/atomics.md for a side-by-side comparison with std/atomics, the DWCAS surface overview, memory-order policy, cross-compiler/arch compatibility matrix, and an LCRQ-style worked example.

Backend strategy: wrap GCC/Clang __atomic_*_n builtins for the unix toolchains (gcc, clang, llvm_gcc, nintendoswitch), and MSVC's _Interlocked* intrinsics family (declared in <intrin.h>) for vcc. The MSVC arms cover the full 1-, 2-, 4-, and 8-byte Atomic[T] surface plus AtomicFlag, fences, and the 16-byte DWCAS via _InterlockedCompareExchange128.

DWCAS (16-byte) emit logic adapted from atomic128 (https://github.com/patternnoster/atomic128) by patternnoster, MIT licensed. See atomic128_ref.hpp for the C++ reference implementation that documents the GCC __sync vs __atomic footgun this code works around. Pinned upstream commit: d45ba3d348a9620a25552f9cf50dc7ccef05ef90. See THIRD_PARTY_LICENSES.md for the verbatim MIT text.

MemoryOrder

type MemoryOrder = enum

Specifies how non-atomic operations can be reordered around atomic

operations. Ordinals match GCC's __ATOMIC_* so ord(order) is passed directly to the builtins.

Values

  • moRelaxed – No ordering constraints.
  • moConsume – Accepted, mapped to moAcquire (mirrors std).
  • moAcquire – No reordering of subsequent loads/stores before.
  • moRelease – No reordering of preceding loads/stores after.
  • moAcquireRelease – Both acquire and release on RMW.
  • moSequentiallyConsistent – Single total order across all SC ops.

cacheLineAligned

template cacheLineAligned(decl: untyped)

Drop-in {.align: CacheLineBytes.} shorthand.

Parameters
  • decl (untyped)

Pair

type Pair[A, B] = object

16-byte-aligned object wrapper for DWCAS. Both halves must satisfy

supportsCopyMem, each must be <= 8 bytes, and sizeof(A) + sizeof(B) must equal 16. Domain-neutral field names: first and second.

Conventional LCRQ usage spells Pair[uint64, T] where first is a monotonically-bumped sequence counter (ABA defense) and second is the payload. Generation overflow is documented as impossible in practical lifetimes (uint64 monotonicity).

Field-level {.align: 16.} on first* elevates the whole object to 16-byte alignment per Nim 2.2.10 align-pragma scope rules; the object-level form (type Pair {.align: 16.} = object ...) is rejected.

ptr T fields are explicitly opt-out of ARC: Pair makes no claim on the lifetime of whatever a contained ptr points at.

See docs/guide/atomics.md §7 for an LCRQ-style worked example using Pair[uint64, ptr Node].

Fields

  • second B

hasManagedFields compileTime

proc hasManagedFields(T: typedesc): bool
Parameters
  • T (typedesc)
Returns

bool

enforceDwcasConstraints

template enforceDwcasConstraints(A, B: typedesc)

Gate 2 (Pair shape) + Gate 4 (lock-free) for Atomic[Pair[A, B]].

Checks sizeof(A) + sizeof(B) == 16 rather than sizeof(Pair[A, B]) == 16: the field-level {.align: 16.} on first pads any undersized Pair up to 16 bytes, so the outer sizeof would silently mask half-size mismatches (e.g. Pair[uint64, uint32] has 12 bytes of payload + 4 padding). DWCAS requires both halves live (cmpxchg16b / casp compares all 128 bits), so the payload sum is the real safety invariant.

Parameters
  • A (typedesc)
  • B (typedesc)

Atomic

type Atomic[T] = object

Atomic wrapper for T. Lock-free on this target; rejects

ref T.

Supports 1-, 2-, 4-, and 8-byte types only. 16-byte types (__int128, double-quadword pointers) require a different code path (cmpxchg16b on x86_64, casp on aarch64) and are not currently provided. Such instantiations fail with a compile-time {.error.} from the underlying nonAtomicType template.

Note on alignment: Nim's field-level {.align: ...} pragma cannot reference sizeof(T) from a generic context (as of Nim 2.2.6 it triggers sizeof requires .importc types to be .completeStruct). We therefore rely on T's natural alignment — which is >= sizeof(T) for the primitives we ship on every 64-bit ABI we currently target, but is not universally true. Notably, on i386 System V alignof(uint64) == 4, which would yield a split-lock object that is not always-lock-free.

enforceAtomicConstraints therefore fires static: assert alignof(Atomic[T]) >= sizeof(T) per instantiation; a target where natural alignment is insufficient fails to compile rather than silently producing a non-lock-free object. If we ever need to support such a target, the fix is a per-size specialisation that boxes T in a struct with an explicit {.align: 8.} (or similar) field; the generic path cannot do better today.

nonAtomicType

template nonAtomicType(T: typedesc): typedesc
Parameters
  • T (typedesc)
Returns

typedesc

load inline

proc load(loc: var Atomic[T]; order: static MemoryOrder = moSequentiallyConsistent): T
Parameters
  • loc (var Atomic[T])
  • order (static MemoryOrder)
Returns

T

store inline

proc store(loc: var Atomic[T]; desired: T; order: static MemoryOrder = moSequentiallyConsistent)
Parameters
  • loc (var Atomic[T])
  • desired (T)
  • order (static MemoryOrder)

exchange inline

proc exchange(loc: var Atomic[T]; desired: T; order: static MemoryOrder = moSequentiallyConsistent): T
Parameters
  • loc (var Atomic[T])
  • desired (T)
  • order (static MemoryOrder)
Returns

T

compareExchangeStrong inline

proc compareExchangeStrong(loc: var Atomic[T]; expected: var T; desired: T; success: static MemoryOrder; failure: static MemoryOrder): bool

Strong CAS. On success, swaps desired into loc. On failure,

overwrites expected with the current value of loc.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
  • success (static MemoryOrder)
  • failure (static MemoryOrder)
Returns

bool

compareExchangeWeak inline

proc compareExchangeWeak(loc: var Atomic[T]; expected: var T; desired: T; success: static MemoryOrder; failure: static MemoryOrder): bool

Weak CAS. May fail spuriously on platforms (notably ARM LL/SC)

even when current value equals expected. Cheaper inside a loop.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
  • success (static MemoryOrder)
  • failure (static MemoryOrder)
Returns

bool

compareExchangeStrong inline

proc compareExchangeStrong(loc: var Atomic[T]; expected: var T; desired: T; order: static MemoryOrder): bool

Strong CAS, single-order form. Failure order is derived from

success per C11 (drop the release component): moRelease -> moRelaxed, moAcquireRelease -> moAcquire, otherwise unchanged. Use the five-arg form to spell the failure order explicitly.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
  • order (static MemoryOrder)
Returns

bool

compareExchangeWeak inline

proc compareExchangeWeak(loc: var Atomic[T]; expected: var T; desired: T; order: static MemoryOrder): bool

Weak CAS, single-order form. Failure order is derived from

success per compareExchangeStrong's single-order overload.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
  • order (static MemoryOrder)
Returns

bool

compareExchangeStrong inline

proc compareExchangeStrong(loc: var Atomic[T]; expected: var T; desired: T): bool

Strong CAS, default-order form. Equivalent to passing

moSequentiallyConsistent for both success and failure.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
Returns

bool

compareExchangeWeak inline

proc compareExchangeWeak(loc: var Atomic[T]; expected: var T; desired: T): bool

Weak CAS, default-order form. Equivalent to passing

moSequentiallyConsistent for both success and failure.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
Returns

bool

compareExchange inline

proc compareExchange(loc: var Atomic[T]; expected: var T; desired: T; success: static MemoryOrder; failure: static MemoryOrder): bool

Strong compare-and-exchange. Unsuffixed-name alias for

compareExchangeStrong, matching the spelling used by clients migrating from std/atomics.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
  • success (static MemoryOrder)
  • failure (static MemoryOrder)
Returns

bool

compareExchange inline

proc compareExchange(loc: var Atomic[T]; expected: var T; desired: T; order: static MemoryOrder): bool

Strong CAS, single-order form. Failure order is derived from

success per C11 (drop the release component). Alias for compareExchangeStrong.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
  • order (static MemoryOrder)
Returns

bool

compareExchange inline

proc compareExchange(loc: var Atomic[T]; expected: var T; desired: T): bool

Strong CAS, default-order form. Equivalent to passing

moSequentiallyConsistent for both success and failure. Alias for compareExchangeStrong.

Parameters
  • loc (var Atomic[T])
  • expected (var T)
  • desired (T)
Returns

bool

fetchAdd inline

proc fetchAdd(loc: var Atomic[T]; v: T; order: static MemoryOrder = moSequentiallyConsistent): T
Parameters
  • loc (var Atomic[T])
  • v (T)
  • order (static MemoryOrder)
Returns

T

fetchSub inline

proc fetchSub(loc: var Atomic[T]; v: T; order: static MemoryOrder = moSequentiallyConsistent): T
Parameters
  • loc (var Atomic[T])
  • v (T)
  • order (static MemoryOrder)
Returns

T

fetchAnd inline

proc fetchAnd(loc: var Atomic[T]; v: T; order: static MemoryOrder = moSequentiallyConsistent): T
Parameters
  • loc (var Atomic[T])
  • v (T)
  • order (static MemoryOrder)
Returns

T

fetchOr inline

proc fetchOr(loc: var Atomic[T]; v: T; order: static MemoryOrder = moSequentiallyConsistent): T
Parameters
  • loc (var Atomic[T])
  • v (T)
  • order (static MemoryOrder)
Returns

T

fetchXor inline

proc fetchXor(loc: var Atomic[T]; v: T; order: static MemoryOrder = moSequentiallyConsistent): T
Parameters
  • loc (var Atomic[T])
  • v (T)
  • order (static MemoryOrder)
Returns

T

fetchAdd inline

proc fetchAdd(loc: var Atomic[T]; v: T; order: static MemoryOrder = moSequentiallyConsistent): T

Atomically add v to the float value at loc and return the

previous value. Implemented as a compareExchangeWeak CAS-loop because GCC's __atomic_fetch_add_n is integer-only.

Semantics: the read-add-CAS cycle is repeated until the CAS succeeds, so the returned old value and the new stored value together reflect a coherent atomic update of loc. Under contention, the loop may iterate multiple times.

order is applied to the successful CAS; the failure order is derived per C11 (drops the release component).

Bit-pattern fidelity caveat: fetchAdd performs an IEEE-754 float addition; unlike load/store/exchange/compareExchange* (pure bitwise transfer), the bit-pattern guarantees do NOT apply to the new stored value. Specifically:

  • The returned old value IS bit-exact: it comes from a relaxed load before the add and reflects the pre-RMW storage bits verbatim (so e.g. a NaN payload in loc is preserved in the return value).
  • The new stored value is old + v computed by the FPU, which means: NaN payloads are not preserved across the add (e.g. NaN_payload_A + 1.0 yields a quiet NaN with implementation- defined payload, not payload_A); denormal results may flush to zero if FTZ/DAZ is enabled in the calling thread's FPU state; the rounding mode is whatever the FPU is currently set to (round-to-nearest by default; modifiable via fesetround on x86 or FPCR on ARM); and overflow produces +/-Inf.

The CAS-loop preserves atomicity; these caveats are inherent to float arithmetic, not to this implementation.

Parameters
  • loc (var Atomic[T])
  • v (T)
  • order (static MemoryOrder)
Returns

T

threadFence inline

proc threadFence(order: MemoryOrder)

Full memory fence between threads. All memory orders are valid.

Parameters
  • order (MemoryOrder)

signalFence inline

proc signalFence(order: MemoryOrder)

Compiler-only fence. Prevents the compiler from reordering across

the fence; emits no CPU instructions. All memory orders are valid.

Parameters
  • order (MemoryOrder)

AtomicFlag

type AtomicFlag = distinct uint8

Boolean flag with testAndSet / clear semantics. Underlying

byte must be 0 or 1; __atomic_test_and_set is implementation-defined for any other value, so do not poke the raw uint8 directly.

testAndSet inline

proc testAndSet(loc: var AtomicFlag; order: static MemoryOrder = moSequentiallyConsistent): bool

Atomically set the flag and return its previous value.

Parameters
  • loc (var AtomicFlag)
  • order (static MemoryOrder)
Returns

bool

clear inline

proc clear(loc: var AtomicFlag; order: static MemoryOrder = moSequentiallyConsistent)

Atomically reset the flag to false. order must not be

moAcquire / moAcquireRelease / moConsume.

Parameters
  • loc (var AtomicFlag)
  • order (static MemoryOrder)

dwcasLoad

template dwcasLoad(loc: var Atomic[Pair[A, B]]; order: static MemoryOrder): Pair[A, B]

Low-level 16-byte atomic load emit. Backend-dispatched by compiler:

gcc → __sync_val_compare_and_swap on __int128 (inlines cmpxchg16b on x86_64 with -mcx16 and casp on aarch64 with -march=armv8.1-a+lse); clang / llvm_gcc → __atomic_load_n on __int128. Callers should prefer load(Atomic[Pair[A, B]]) which carries the per-callsite memory-order validation and seq_cst-upgrade warning. Exposed for testing and for callers that have already validated the order policy externally.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • order (static MemoryOrder)
Returns

Pair[A, B]

load inline

proc load(loc: var Atomic[Pair[A, B]]; order: static MemoryOrder = moSequentiallyConsistent): Pair[A, B]

16-byte atomic load via DWCAS substrate. Returns the current value

of loc as a Pair[A, B]. Always seq_cst at the instruction level; sub-seq_cst order values are accepted but upgraded with a compile- time warning.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • order (static MemoryOrder)
Returns

Pair[A, B]

dwcasStore

template dwcasStore(loc: var Atomic[Pair[A, B]]; desired: Pair[A, B]; order: static MemoryOrder)

Low-level 16-byte atomic store emit. Backend-dispatched (see

dwcasLoad). Prefer store(Atomic[Pair[A, B]]) for the validated, warning-emitting surface.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • desired (Pair[A, B])
  • order (static MemoryOrder)

store inline

proc store(loc: var Atomic[Pair[A, B]]; desired: Pair[A, B]; order: static MemoryOrder = moSequentiallyConsistent)

16-byte atomic store via DWCAS substrate. Always seq_cst at the

instruction level; sub-seq_cst order values emit a compile-time warning.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • desired (Pair[A, B])
  • order (static MemoryOrder)

dwcasExchange

template dwcasExchange(loc: var Atomic[Pair[A, B]]; desired: Pair[A, B]; order: static MemoryOrder): Pair[A, B]

Low-level 16-byte atomic exchange emit. Backend-dispatched

(see dwcasLoad). Prefer exchange(Atomic[Pair[A, B]]) for the validated, warning-emitting surface.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • desired (Pair[A, B])
  • order (static MemoryOrder)
Returns

Pair[A, B]

exchange inline

proc exchange(loc: var Atomic[Pair[A, B]]; desired: Pair[A, B]; order: static MemoryOrder = moSequentiallyConsistent): Pair[A, B]

16-byte atomic exchange via DWCAS substrate. Atomically replaces the

value at loc with desired and returns the prior value. Always seq_cst at the instruction level; sub-seq_cst order values emit a compile-time warning.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • desired (Pair[A, B])
  • order (static MemoryOrder)
Returns

Pair[A, B]

dwcasCasStrong

template dwcasCasStrong(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]; success: static MemoryOrder; failure: static MemoryOrder): bool

Low-level 16-byte strong CAS emit. Backend-dispatched (see

dwcasLoad). On gcc maps to __sync_val_compare_and_swap (always-strong); on clang / llvm_gcc maps to __atomic_compare_exchange_n with the weak flag = 0. Prefer compareExchangeStrong(Atomic[Pair[A, B]]) for the validated, warning-emitting surface.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
  • success (static MemoryOrder)
  • failure (static MemoryOrder)
Returns

bool

compareExchangeStrong inline

proc compareExchangeStrong(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]; success: static MemoryOrder; failure: static MemoryOrder): bool

Strong 16-byte CAS via DWCAS. On success, swaps desired into

loc and returns true; expected is unchanged. On failure, overwrites expected with the current value of loc and returns false. Always seq_cst at the instruction level; sub-seq_cst success/failure values emit a compile-time warning.

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
  • success (static MemoryOrder)
  • failure (static MemoryOrder)
Returns

bool

compareExchangeStrong inline

proc compareExchangeStrong(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]; order: static MemoryOrder): bool

Strong 16-byte CAS, single-order form. Failure order is derived

from order per C11 (drop the release component).

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
  • order (static MemoryOrder)
Returns

bool

compareExchangeStrong inline

proc compareExchangeStrong(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]): bool

Strong 16-byte CAS, default-order form. Equivalent to passing

moSequentiallyConsistent for both success and failure.

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
Returns

bool

dwcasCasWeak

template dwcasCasWeak(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]; success: static MemoryOrder; failure: static MemoryOrder): bool

Low-level 16-byte weak CAS emit. Backend-dispatched (see

dwcasLoad). On gcc, __sync_val_compare_and_swap is always-strong on both x86_64 (cmpxchg16b) and aarch64+LSE (casp), so the weak/strong distinction is a no-op; on clang / llvm_gcc maps to __atomic_compare_exchange_n with weak=1, where ARMv8.0 LL/SC (no LSE) stlxp may genuinely fail spuriously, while aarch64 FEAT_LSE / LSE2 (caspal, objdump-verified on Apple Silicon) is always-strong. Prefer compareExchangeWeak(Atomic[Pair[A, B]]) for the validated, warning-emitting surface.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
  • success (static MemoryOrder)
  • failure (static MemoryOrder)
Returns

bool

compareExchangeWeak inline

proc compareExchangeWeak(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]; success: static MemoryOrder; failure: static MemoryOrder): bool

Weak 16-byte CAS via DWCAS. May fail spuriously only on ARMv8.0

LL/SC cores (no LSE), where stlxp is permitted to fail without contention. On all other supported targets weak and strong are equivalent: x86_64 (cmpxchg16b) is always-strong; arm64 with FEAT_LSE / LSE2 (Apple Silicon, modern server chips) emits caspal for both procs (objdump-verified on Apple Silicon). Default to compareExchangeStrong; reach for Weak only after measuring a contention win on an ARMv8.0 target.

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
  • success (static MemoryOrder)
  • failure (static MemoryOrder)
Returns

bool

compareExchangeWeak inline

proc compareExchangeWeak(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]; order: static MemoryOrder): bool

Weak 16-byte CAS, single-order form. Failure order is derived

from order per C11 (drop the release component).

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
  • order (static MemoryOrder)
Returns

bool

compareExchangeWeak inline

proc compareExchangeWeak(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]): bool

Weak 16-byte CAS, default-order form. Equivalent to passing

moSequentiallyConsistent for both success and failure. Mirrors the compareExchangeStrong default-order overload so that the two overload sets match shape-for-shape — relevant for callers migrating from std/atomics, whose compareExchange* procs all accept the no-order form.

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
Returns

bool

compareExchange inline

proc compareExchange(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]; success: static MemoryOrder; failure: static MemoryOrder): bool

Strong 16-byte CAS. Unsuffixed-name alias for

compareExchangeStrong, matching the spelling used by clients migrating from std/atomics.

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
  • success (static MemoryOrder)
  • failure (static MemoryOrder)
Returns

bool

compareExchange inline

proc compareExchange(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]; order: static MemoryOrder): bool

Strong 16-byte CAS, single-order form. Alias for

compareExchangeStrong.

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
  • order (static MemoryOrder)
Returns

bool

compareExchange inline

proc compareExchange(loc: var Atomic[Pair[A, B]]; expected: var Pair[A, B]; desired: Pair[A, B]): bool

Strong 16-byte CAS, default-order form. Alias for

compareExchangeStrong.

ABA / aliasing note: expected and desired MUST be distinct memory locations. On CAS failure, expected is overwritten in-place with the current value of loc; on success, expected is unchanged. Passing the same var location for both expected and desired is a defined-but-confusing pattern (the desired payload is read first, then expected is overwritten — but if expected and desired alias, the post-CAS desired is undefined). Callers MUST NOT alias them. The 1/2/4/8-byte CAS surface has the same contract; the 16-byte ops re-document it here because the wider value makes accidental aliasing more tempting in LCRQ-style consumer code.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • expected (var Pair[A, B])
  • desired (Pair[A, B])
Returns

bool

fetchAdd inline

proc fetchAdd(loc: var Atomic[Pair[A, B]]; delta: Pair[A, B]; order: static MemoryOrder = moSequentiallyConsistent): Pair[A, B]

Atomically add delta.first to loc.first and delta.second to

loc.second, returning the prior 128-bit pair. Both halves are updated as a single atomic transaction via a 16-byte CAS-loop (cmpxchg16b / casp / _InterlockedCompareExchange128).

Half-type overflow behavior follows the underlying integer type (SomeInteger). Unsigned halves wrap modulo 2^(sizeof(half)*8). Signed halves do NOT silently wrap: the half-add prev.first + delta.first is an ordinary checked Nim addition, so under default builds (overflow checks on) a signed-half overflow raises OverflowDefect — aborting the CAS loop rather than completing the "atomic transaction". Signed halves only wrap two's-complement under -d:danger or an explicit {.push overflowChecks: off.}. For counters that must never trap (e.g. an LCRQ generation tag), use an unsigned half-type.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • delta (Pair[A, B])
  • order (static MemoryOrder)
Returns

Pair[A, B]

fetchSub inline

proc fetchSub(loc: var Atomic[Pair[A, B]]; delta: Pair[A, B]; order: static MemoryOrder = moSequentiallyConsistent): Pair[A, B]

Componentwise atomic subtract. See fetchAdd for transactional

semantics and overflow behavior.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • delta (Pair[A, B])
  • order (static MemoryOrder)
Returns

Pair[A, B]

fetchAnd inline

proc fetchAnd(loc: var Atomic[Pair[A, B]]; mask: Pair[A, B]; order: static MemoryOrder = moSequentiallyConsistent): Pair[A, B]

Componentwise atomic bitwise AND. See fetchAdd for transactional

semantics.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • mask (Pair[A, B])
  • order (static MemoryOrder)
Returns

Pair[A, B]

fetchOr inline

proc fetchOr(loc: var Atomic[Pair[A, B]]; mask: Pair[A, B]; order: static MemoryOrder = moSequentiallyConsistent): Pair[A, B]

Componentwise atomic bitwise OR. See fetchAdd for transactional

semantics.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • mask (Pair[A, B])
  • order (static MemoryOrder)
Returns

Pair[A, B]

fetchXor inline

proc fetchXor(loc: var Atomic[Pair[A, B]]; mask: Pair[A, B]; order: static MemoryOrder = moSequentiallyConsistent): Pair[A, B]

Componentwise atomic bitwise XOR. See fetchAdd for transactional

semantics.

Parameters
  • loc (var Atomic[Pair[A, B]])
  • mask (Pair[A, B])
  • order (static MemoryOrder)
Returns

Pair[A, B]

dwcasOrderRelaxedCAS

template dwcasOrderRelaxedCAS(body: untyped): untyped

Wraps a DWCAS call site, suppressing the moSeqCst-upgrade

warning emitted by the 16-byte ops (load / store / exchange / compareExchange*) for that site only. Use when the upgrade is intentional and audited (e.g. the LCRQ producer publish CAS passing moRelease / moRelaxed). The warning continues to fire at unwrapped call sites.

See docs/design/2026-04-25-custom-atomics.md §3 and the user guide docs/guide/atomics.md §5 (Memory-order policy) for the memory-model rationale.

Parameters
  • body (untyped)
Returns

untyped