Atomics

An ordinary load or store may be split, reordered or removed by the compiler and the processor. That is harmless while one thread owns the memory, and wrong the moment two threads share it. The atomic operations in Core are compiled to the instructions each architecture provides for shared memory, so no thread ever sees one half done.

import Core::{ AtomicFetchAdd, AtomicLoad, MemoryOrder };

var Hits: uint64 = 0;

func Record() {
    AtomicFetchAdd<uint64>(@Hits, 1, MemoryOrder::Relaxed);
}

func Total() -> uint64 {
    return AtomicLoad<uint64>(@Hits, MemoryOrder::Acquire);
}

They act on one value of 8, 16, 32 or 64 bits — an integer, a bool or a pointer — through its address, which must be aligned to its size. Every thread that touches the value must reach it only through atomic operations. The Rux/Atomic package wraps them in an Atomic<T> type; they live in Core because only the compiler can emit them.

Operations

IntrinsicDoes, as one indivisible stepReturns
AtomicLoad<T>(address, order)reads the valuethe value
AtomicStore<T>(address, value, order)writes valuenothing
AtomicExchange<T>(address, value, order)writes valuethe value it replaced
AtomicCompareExchange<T>(address, expected, desired, success, failure)writes desired if the value is expectedthe value it found
AtomicFetchAdd, AtomicFetchSubadds or subtracts, wrapping on overflow (integers only)the value before
AtomicFetchAnd, AtomicFetchOr, AtomicFetchXorcombines bitwise (integers and bool)the value before

AtomicCompareExchange wrote exactly when the value it returns equals expected, so a retry loop starts again from what was really there. It never fails spuriously.

Memory orderings

Every operation takes a MemoryOrder, which says which of the thread's other memory operations may be reordered across it:

OrderGuarantees
Relaxedthe operation itself is atomic; nothing around it is held back — right for a counter no other memory depends on
Acquirelater operations are not moved before this load, so what a releasing thread wrote before its release is visible
Releaseearlier operations are not moved after this store, so they are visible to a thread that acquires what it stores
AcquireReleaseboth, for a read-modify-write operation that reads and writes
SequentiallyConsistentAcquireRelease, and one order of every such operation that all threads agree on

The weakest ordering that keeps a program correct is also the fastest, and a stronger one is never wrong. When in doubt, use SequentiallyConsistent.

The order is written at the call as a MemoryOrder case, because the compiler chooses the instructions from it while compiling. A value computed at run time is refused:

error: the memory order of 'AtomicLoad' must be written as a 'MemoryOrder' case
  help: write the order at the call, such as 'MemoryOrder::Acquire'

A load cannot release and a store cannot acquire, so AtomicLoad takes only Relaxed, Acquire or SequentiallyConsistent, and AtomicStore only Relaxed, Release or SequentiallyConsistent:

error: 'AtomicStore' cannot use memory order 'Acquire'
  note: a store only writes, so it cannot acquire

For AtomicCompareExchange, success orders the operation when it writes and failure when it only reads; failure may not be Release or AcquireRelease, nor acquire more than success does.

Fences and spin hints

Fence(order) orders the thread's memory operations without touching memory, for code that pairs relaxed operations with one ordering point. SpinHint() tells the processor the thread is waiting in a loop for another thread: it saves power and lets a sibling hardware thread run, and has no effect on memory. A loop that may wait long should block instead, through the Rux/Sync package.

On each architecture

On x86-64, loads and weaker stores are plain mov instructions and the other operations are lock-prefixed. On AArch64, the acquire and release forms of loads and stores are used, and read-modify-write operations run in exclusive-access loops.

See also

  • Module variables — storage every thread shares, and thread var for storage each thread owns
  • Layout — sizes and alignments