Skip to content
CryoCryo home
Stdlibsync

atomic

import std::sync::atomic; · source

Types

Atomic<T>

type struct Atomic<T> {
    value: T;

    static new(initial: T) -> Atomic<T>;
    load(&this, order: MemoryOrder) -> T;
    store(mut &this, val: T, order: MemoryOrder) -> void;
    fetch_add(mut &this, val: T, order: MemoryOrder) -> T;
    fetch_sub(mut &this, val: T, order: MemoryOrder) -> T;
    fetch_and(mut &this, val: T, order: MemoryOrder) -> T;
    fetch_or(mut &this, val: T, order: MemoryOrder) -> T;
    fetch_xor(mut &this, val: T, order: MemoryOrder) -> T;
    swap(mut &this, val: T, order: MemoryOrder) -> T;
    compare_exchange(mut &this, current: T, next: T, succ: MemoryOrder, fail: MemoryOrder) -> Result<T, T>;
}

One generic cell driven by the compiler's atomic intrinsics, which lower directly to LLVM's atomicrmw, cmpxchg, and atomic load/store. No libc, no spin loops, no calls.

import std::sync::atomic;

mut hits: Atomic<u64> = Atomic::<u64>::new(0);
hits.fetch_add(1, MemoryOrder::Relaxed);
const total: u64 = hits.load(MemoryOrder::SeqCst);

T is dispatched at compile time with static match (T), so Atomic::<u32>::load lowers to exactly the u32 atomic and nothing else. LLVM does not distinguish signed from unsigned for atomic integer memory operations, so the i32, i64, and boolean arms bitcast onto the same unsigned intrinsics.

Supported types are u8, u32, u64, i32, i64, and boolean. Instantiating Atomic<T> with anything else — or calling fetch_add on Atomic<boolean> — is a compile error (E0645). The absence of a matching static match arm is the type constraint; no trait bound is needed.

load and store take the ordering explicitly. The fetch_* family and swap return the previous value; compare_exchange(current, next, succ, fail) returns Ok(prev) when it swapped and Err(actual) on a mismatch.

Atomic cells are deliberately not Copy. Mutation through a shared pointer has to stay explicit, and bitwise-copying an atomic would silently break that. Wrap in Arc<Atomic<T>> to share.

Properties

PropertyType
valueT

Constructors

static new(initial: T) -> Atomic<T>;

Construct an Atomic<T> initialized to initial. Stack-allocated; callers wishing to share between threads should wrap in Arc<Atomic<T>> or place inside a heap-pinned container.

Methods

load(&this, order: MemoryOrder) -> T;
store(mut &this, val: T, order: MemoryOrder) -> void;
fetch_add(mut &this, val: T, order: MemoryOrder) -> T;

Atomically add val to the cell and return the previous value.

fetch_sub(mut &this, val: T, order: MemoryOrder) -> T;

Atomically subtract val from the cell and return the previous value. See fetch_add for the Atomic<boolean> caveat.

fetch_and(mut &this, val: T, order: MemoryOrder) -> T;

AND the cell with val. Returns the previous value.

fetch_or(mut &this, val: T, order: MemoryOrder) -> T;

OR the cell with val. Returns the previous value.

fetch_xor(mut &this, val: T, order: MemoryOrder) -> T;

XOR the cell with val. Returns the previous value.

swap(mut &this, val: T, order: MemoryOrder) -> T;

Atomically replace the cell's value with val and return the previous value.

compare_exchange(mut &this, current: T, next: T, succ: MemoryOrder, fail: MemoryOrder) -> Result<T, T>;

Atomic compare-and-swap. Returns Ok(prev) with prev == current when the swap happens; Err(actual) with the observed value when the comparison fails.

Trait implementations

implement<T> trait Drop for struct Atomic<T>

A no-op; it exists so Atomic<T> satisfies T: Drop bounds inside containers.

MemoryOrder

type enum MemoryOrder : u32 {
    Relaxed = 2;
    Acquire = 4;
    Release = 5;
    AcqRel = 6;
    SeqCst = 7;
}

implement enum MemoryOrder {
    static to_u32(order: MemoryOrder) -> u32;
}

When in doubt, SeqCst is always safe. A load cannot use Release. The numeric values are LLVM's ordering codes, which is why the enum starts at 2; the name avoids clashing with core::cmp::Ordering.

Variants

Variant
Relaxed = 2
Acquire = 4
Release = 5
AcqRel = 6
SeqCst = 7

Static functions

static to_u32(order: MemoryOrder) -> u32;

Free Functions

Fences

fence(order) emits a real LLVM fence. Because a Relaxed fence would be meaningless, a weaker request is clamped up to SeqCst rather than producing invalid IR. compiler_fence(order) is for constraining the compiler's reordering rather than the CPU's — signal handlers, longjmp paths — though today it conservatively lowers to a real fence too.

function fence(order: MemoryOrder) -> void;

Insert a memory fence with the given ordering. Equivalent to atomicrmw xchg on a dummy location with no actual ops, but generated as a real LLVM fence instruction.

function compiler_fence(order: MemoryOrder) -> void;

Like fence, but intended only to constrain the compiler's reordering, not the CPU's. Today this conservatively lowers to a real LLVM fence at the requested ordering (so it also bars CPU reordering); the lighter semantics are preserved in the signature for future refinement. Use this when communicating with code inside the same thread (e.g., signal handlers, longjmp paths).