Summary
This proposal introduces atomic variables to Loom, enabling low-level concurrency primitives and memory-safe access to shared data in multithreaded or interrupt-driven environments. Atomic variables are declared with the atomic modifier:
pub var x -> atomic i32;
priv var flag -> atomic bool;
This feature aligns with Loom’s goal of providing fine-grained control over memory, concurrency, and performance on bare-metal architectures.
⸻
Motivation
Atomic operations are fundamental in systems programming for:
• Lock-free data structures
• Spinlocks, semaphores, and wait-free counters
• Hardware flag control and memory-mapped registers
• Interrupt-safe variables and state toggling
• Multicore synchronization
Without native atomic support, Loom developers would have to rely on unsafe compiler intrinsics or manual locking, which contradicts Loom’s design goal of safe and explicit control.
⸻
Guide-Level Explanation
Declaration Syntax
Atomic variables use the standard Loom field declaration syntax with the atomic modifier:
pub var count -> atomic i32;
priv var locked -> atomic bool;
Supported Types
Initially, only basic primitives may be declared as atomic:
• atomic bool
• atomic i8, atomic i16, atomic i32, atomic i64
• atomic u8, atomic u16, atomic u32, atomic u64
Future extensions may include atomic usize, atomic isize, or even custom atomic wrappers.
Usage
pub func main() {
var counter -> atomic i32;
counter.store(1);
var value = counter.load();
counter.fetchAdd(1);
counter.compareExchange(2, 3);
}
Supported Methods
Atomic values expose explicit methods:
• load(ordering?)
• store(value, ordering?)
• exchange(value, ordering?)
• compareExchange(expected, new, ordering?)
• fetchAdd(value, ordering?)
• fetchSub(value, ordering?)
• fetchAnd(value, ordering?)
• fetchOr(value, ordering?)
• fetchXor(value, ordering?)
Memory Ordering (Optional)
Optionally specify memory ordering:
counter.store(42, relaxed);
let x = counter.load(acquire);
Available ordering modes:
• relaxed
• acquire
• release
• acq_rel
• seq_cst (default)
⸻
Reference-Level Explanation
Grammar Changes
Type ::= "atomic" PrimitiveType
PrimitiveType ::= "bool" | "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64"
Field and variable declarations can include -> atomic T where T is a supported primitive type.
Type System
• Atomic variables are distinct types from their non-atomic equivalents (e.g., atomic i32 ≠ i32)
• Cannot perform direct arithmetic (+, -, etc.) unless via atomic methods
• Comparisons and loads return the base type (i32, etc.)
Operator Restrictions
• Arithmetic operators (+=, -=) are disallowed by default
• Optional: allow sugar for atomic ops only on atomic types with strict desugaring
counter += 1; // desugars to counter.fetchAdd(1)
This sugar must not be available to non-atomic types to avoid ambiguity.
IR / Codegen
Emit platform-specific atomic instructions:
• x86: lock xadd, cmpxchg, mov with memory ordering fences
• ARM: LDREX, STREX, DMB, DSB
• Fallback: critical sections or spinlocks (for unsupported platforms)
Ensure atomicity is preserved even with aggressive compiler optimizations.
⸻
Drawbacks
Concern Details
Complexity Increases type and memory model complexity
Misuse Developers may misuse atomic types without understanding memory ordering
Portability Not all targets support all atomic widths (e.g., atomic i64 on 32-bit MCUs)
Performance Incorrect ordering may degrade performance or introduce contention
⸻
Alternatives
• Use an Atomic wrapper class — more verbose, less ergonomic
• Rely on unsafe blocks and inline assembly — less safe, not Loom-style
• External libraries — not viable for core concurrency features
⸻
Prior Art
Language Atomic Support Notes
C/C++ std::atomic Verbose but flexible with full memory model support
Rust AtomicI32, Ordering Type-safe, explicit, fine-grained
Zig @atomicLoad, @cmpxchgStrong Intrinsic-based with explicit memory models
Java volatile, AtomicInteger High-level but GC-dependent
Go sync/atomic Package-based; function wrappers only
⸻
Unresolved Questions
• Should operator sugar be included or kept out of MVP?
• Should memory ordering keywords be lowercase (relaxed) or namespaced (Ordering::Relaxed)?
• Should atomic types implement traits or interfaces (AtomicOps)?
⸻
Future Directions
• Add support for atomic usize, isize based on platform word size
• Add higher-level atomic types (e.g., AtomicRef, AtomicFlag)
• Add atomic wait/notify primitives (wait, notifyOne, notifyAll)
• Add memory fences (fence(Ordering))
20250806-atomic-variablesSummary
This proposal introduces atomic variables to Loom, enabling low-level concurrency primitives and memory-safe access to shared data in multithreaded or interrupt-driven environments. Atomic variables are declared with the
atomicmodifier:This feature aligns with Loom’s goal of providing fine-grained control over memory, concurrency, and performance on bare-metal architectures.
⸻
Motivation
Atomic operations are fundamental in systems programming for:
• Lock-free data structures
• Spinlocks, semaphores, and wait-free counters
• Hardware flag control and memory-mapped registers
• Interrupt-safe variables and state toggling
• Multicore synchronization
Without native atomic support, Loom developers would have to rely on unsafe compiler intrinsics or manual locking, which contradicts Loom’s design goal of safe and explicit control.
⸻
Guide-Level Explanation
Declaration Syntax
Atomic variables use the standard Loom field declaration syntax with the atomic modifier:
Supported Types
Initially, only basic primitives may be declared as atomic:
• atomic bool
• atomic i8, atomic i16, atomic i32, atomic i64
• atomic u8, atomic u16, atomic u32, atomic u64
Future extensions may include atomic usize, atomic isize, or even custom atomic wrappers.
Usage
Supported Methods
Atomic values expose explicit methods:
• load(ordering?)
• store(value, ordering?)
• exchange(value, ordering?)
• compareExchange(expected, new, ordering?)
• fetchAdd(value, ordering?)
• fetchSub(value, ordering?)
• fetchAnd(value, ordering?)
• fetchOr(value, ordering?)
• fetchXor(value, ordering?)
Memory Ordering (Optional)
Optionally specify memory ordering:
Available ordering modes:
• relaxed
• acquire
• release
• acq_rel
• seq_cst (default)
⸻
Reference-Level Explanation
Grammar Changes
Field and variable declarations can include -> atomic T where T is a supported primitive type.
Type System
• Atomic variables are distinct types from their non-atomic equivalents (e.g., atomic i32 ≠ i32)
• Cannot perform direct arithmetic (+, -, etc.) unless via atomic methods
• Comparisons and loads return the base type (i32, etc.)
Operator Restrictions
• Arithmetic operators (+=, -=) are disallowed by default
• Optional: allow sugar for atomic ops only on atomic types with strict desugaring
This sugar must not be available to non-atomic types to avoid ambiguity.
IR / Codegen
Emit platform-specific atomic instructions:
• x86: lock xadd, cmpxchg, mov with memory ordering fences
• ARM: LDREX, STREX, DMB, DSB
• Fallback: critical sections or spinlocks (for unsupported platforms)
Ensure atomicity is preserved even with aggressive compiler optimizations.
⸻
Drawbacks
Concern Details
Complexity Increases type and memory model complexity
Misuse Developers may misuse atomic types without understanding memory ordering
Portability Not all targets support all atomic widths (e.g., atomic i64 on 32-bit MCUs)
Performance Incorrect ordering may degrade performance or introduce contention
⸻
Alternatives
• Use an Atomic wrapper class — more verbose, less ergonomic
• Rely on unsafe blocks and inline assembly — less safe, not Loom-style
• External libraries — not viable for core concurrency features
⸻
Prior Art
Language Atomic Support Notes
C/C++ std::atomic Verbose but flexible with full memory model support
Rust AtomicI32, Ordering Type-safe, explicit, fine-grained
Zig @atomicLoad, @cmpxchgStrong Intrinsic-based with explicit memory models
Java volatile, AtomicInteger High-level but GC-dependent
Go sync/atomic Package-based; function wrappers only
⸻
Unresolved Questions
• Should operator sugar be included or kept out of MVP?
• Should memory ordering keywords be lowercase (relaxed) or namespaced (Ordering::Relaxed)?
• Should atomic types implement traits or interfaces (AtomicOps)?
⸻
Future Directions
• Add support for atomic usize, isize based on platform word size
• Add higher-level atomic types (e.g., AtomicRef, AtomicFlag)
• Add atomic wait/notify primitives (wait, notifyOne, notifyAll)
• Add memory fences (fence(Ordering))