Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Atomics and the Six Orderings

โญโญโญ advancedโณ 20 min read๐Ÿ“ Lesson 208 of 225

Non-atomic data races are undefined behavior; atomics are the language's built-in way to share data across threads with defined visibility.

The problem atomics solve

Two threads incrementing a plain int is a data race โ€” undefined behavior in ISO C (C11 ยง5.1.2.4). The compiler may load once, add twice, store once; the hardware may interleave reads and writes mid-word. Atomics fix this by making read-modify-write indivisible and by letting you choose how strongly visibility propagates.

#include <stdatomic.h>          /* ISO C โ€” not POSIX */

atomic_int counter = 0;         /* initialized to 0, no race on init */

void bump(void) {
    atomic_fetch_add(&counter, 1);   /* returns the PREVIOUS value */
}

atomic_fetch_add returns the value before the addition โ€” the classic x++ race is gone because the whole load-add-store is one atomic operation.

The six orderings, honestly ranked

| ordering | promise | use it when | |----------|---------|-------------| | memory_order_relaxed | atomicity only; no ordering of other memory | statistics counters, sequence numbers | | memory_order_acquire | later reads/writes cannot move before this load | the reading side of a lock-free publish | | memory_order_release | earlier reads/writes cannot move after this store | the writing side of a lock-free publish | | memory_order_acq_rel | both, for read-modify-write ops | CAS loops that read and write | | memory_order_seq_cst | one total order every thread agrees on | default; use unless profiling says otherwise | | memory_order_consume | deprecated in practice; compilers promote to acquire | do not use |

The release/acquire pair is the workhorse: a release store that writes flag=1 synchronizes-with an acquire load that observes flag=1, making everything written before the release visible after the acquire. This sandbox (gcc on aarch64) honors both; ATOMIC_INT_LOCK_FREE == 2 here, so atomic_int needs no lock.

Relaxed is not broken โ€” it is narrower

The default seq_cst is total order across all atomic operations. relaxed keeps atomicity but allows different threads to disagree about the order of different variables. It is the right tool for a miss counter nobody coordinates on โ€” and the wrong tool for publishing a pointer (the data behind the pointer needs the release/acquire fence).

Rule of the module

ISO C atomics define visibility; POSIX pthreads defines threads. pthread_create is POSIX (feature macro required); atomic_int is ISO C. Keep the labels straight and the code portable.

โšก Now practice

Ready to Code
Atomics and Ordering DrillsImplement CAS loops, orderings, and padded counters against the real C11 semantics of this toolchain.
3 challenges ยท ยท ~24 min