std::memory_order

C++‘s enum for controlling which memory reorderings are blocked around an atomic operation. Saw this at Tesla.

What each ordering actually means lives in Memory Consistency Model § Programming against it. This note is just the C++ syntax and idiomatic patterns.

enum memory_order {
    memory_order_relaxed,  // no ordering, just atomicity
    memory_order_consume,  // acquire but only for data-dependent reads (rarely used, often promoted to acquire)
    memory_order_acquire,  // later accesses can't move before this load
    memory_order_release,  // earlier accesses can't move after this store
    memory_order_acq_rel,  // both, for RMW ops like fetch_add
    memory_order_seq_cst   // default: full SC, a single total order across all threads
};

Acquire / release pairing

The bread-and-butter pattern, paired load/store on a flag to publish data:

std::atomic<int> flag{0};
int data;
 
// Thread A
data = 123;
flag.store(1, std::memory_order_release);  // data=123 can't float past this
 
// Thread B
while (flag.load(std::memory_order_acquire) == 0) {}
std::cout << data;  // guaranteed to see 123

Fences

Same effect, but the fence is separate from the atomic operation so the atomic itself can be relaxed:

std::atomic<int> flag{0};
int data;
 
void producer() {
    data = 123;
    std::atomic_thread_fence(std::memory_order_release);
    flag.store(1, std::memory_order_relaxed);
}
 
void consumer() {
    while (flag.load(std::memory_order_relaxed) == 0) {}
    std::atomic_thread_fence(std::memory_order_acquire);
    int x = data;  // guaranteed 123
}