Skip to content

Conversation

@elijahr
Copy link
Contributor

@elijahr elijahr commented Dec 17, 2025

Changes std/atomics to determine lock-free eligibility by size, supportsCopyMem, architecture, and memory management rather than by type.

Previously, only Trivial types (SomeNumber | bool | enum | ptr | pointer) could use lock-free atomics. Any other type (small objects, char, etc) would fall back to spin-lock even though it could fit in a register.

Now, any type with sizeof(T) of 1, 2, 4, 8, or 16 bytes (with architecture support) and no managed memory (under ARC/ORC) uses lock-free atomics.

Dependencies

This PR depends on #25374 which fixes a compiler bug that blocks using when isLockFree(T) in the Atomic[T] body.

Changes

  • Replace Trivial type constraint with public isLockFree(T) template
  • Add hasLockFree8 constant for 8-byte atomic support
  • Add hasLockFree16 constant for 16-byte atomic support
  • Add 128-bit atomic type (Int128) for 16-byte lock-free operations
  • Add -d:nimEnforceLockFreeAtomics to error instead of silently falling back to spinlock
  • Add -d:nimNoLockFree16 to disable 16-byte lock-free for old x86-64 CPUs
  • Document lock-free eligibility rules

Architecture Support

8-byte atomics (hasLockFree8)

Architecture Supported Instruction
All 64-bit Native
x86-32 CMPXCHG8B
ARM32 LDREXD
MIPS32 -
PowerPC32 -
SPARC32 -
RISC-V 32 -

16-byte atomics (hasLockFree16)

Architecture Supported Instruction
x86-64 CMPXCHG16B
ARM64 LDXP/STXP
Other 64-bit -
All 32-bit -

Note: Use -d:nimNoLockFree16 if targeting very old x86-64 CPUs (pre-2005) that lack CMPXCHG16B.

Lock-free eligibility by type and memory manager

Type refc markAndSweep none arc orc atomicArc
int, bool, char, ptr
enum, distinct
Small object (1-4 bytes)
8-byte object* ✅* ✅* ✅* ✅* ✅* ✅*
16-byte object** ✅** ✅** ✅** ✅** ✅** ✅**
ref, string, seq
Big object (>16 bytes)

* Requires hasLockFree8
** Requires hasLockFree16

…uses

The hasValuelessStatics function in semtypinst.nim only checked for tyStatic,
missing tyTypeDesc(tyGenericParam). This caused sizeof(T) inside a typedesc
template called from a generic type's when clause to error with "'sizeof'
requires '.importc' types to be '.completeStruct'".

The fix adds a check for tyTypeDesc wrapping tyGenericParam, recognizing it
as an unresolved generic parameter that needs resolution before evaluation.

Also documents the completeStruct pragma in the manual.
…f type constraint

Changes `std/atomics` to determine lock-free eligibility by size, supportsCopyMem, and memory management rather than by type.

Previously, only `Trivial` types (`SomeNumber | bool | enum | ptr | pointer`) could use lock-free atomics. Any other type (small objects, char, etc) would fall back to spinlock even though it could fit in a register.

Now, any type with `sizeof(T)` of 1, 2, 4, or 8 bytes (and no managed memory under ARC/ORC) uses lock-free atomics.
When using -d:nimUseCppAtomics on Linux, 16-byte atomic operations
via C++ std::atomic also require linking with libatomic, same as
the C11 backend path. This fixes linker errors for undefined
references to __atomic_store_16, __atomic_load_16, etc.
@elijahr
Copy link
Contributor Author

elijahr commented Dec 22, 2025

Converting to draft - still working on the API design.

  • Flipping the default so non-lock-free atomics error at compile time (use -d:nimAllowAtomicSpinlock to opt into spinlock fallback)
  • Also expanding documentation on safe usage with managed types and different memory managers

@elijahr elijahr marked this pull request as draft December 22, 2025 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant