# stdatomic.h **`stdatomic.h`** (C11) gives you atomic types and operations for lock-free concurrent programming. An atomic operation completes without interruption: no other thread can observe the object in a half-updated state. This is the C standard's portable answer to compiler-specific builtins like `__sync_fetch_and_add` and `__atomic_compare_exchange`. If you have ever protected a shared counter with a mutex and thought "this seems like overkill for a single increment", atomics are what you want instead. A mutex involves at least two system calls and kernel transitions; an atomic increment compiles to a single locked CPU instruction. ```c #include atomic_int counter = 0; // from any thread: atomic_fetch_add(&counter, 1); // race-free increment int v = atomic_load(&counter); // race-free read atomic_store(&counter, 0); // race-free write ``` The standard atomic types are `atomic_int`, `atomic_long`, `atomic_uint64_t`, etc., or declared with `_Atomic(type)`. The key operation for lock-free algorithms is compare-and-swap: ```c int expected = 5; bool ok = atomic_compare_exchange_strong(&x, &expected, 10); // if *x == expected: sets *x = 10, returns true // if *x != expected: writes current *x into expected, returns false ``` Every atomic operation has an optional memory order. The default (`memory_order_seq_cst`) is the strongest and most expensive. For performance-critical code, `_explicit` variants let you choose: ```c atomic_store_explicit(&flag, 1, memory_order_release); // publish a write int f = atomic_load_explicit(&flag, memory_order_acquire); // see it atomic_fetch_add_explicit(&x, 1, memory_order_relaxed); // just atomicity, no ordering ``` `release`/`acquire` pairs are sufficient for most producer-consumer patterns. `relaxed` gives only atomicity with no synchronisation between threads. ## Practice ```c // compile: gcc -O2 -o atomicdemo atomicdemo.c -lpthread // run: ./atomicdemo // description: two threads increment a shared counter 1M times each; should total 2M #include #include #include atomic_int counter = 0; void *worker(void *arg) { for (int i = 0; i < 1000000; i++) atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed); return NULL; } int main(void) { pthread_t t1, t2; pthread_create(&t1, NULL, worker, NULL); pthread_create(&t2, NULL, worker, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); printf("counter = %d (expected 2000000)\n", atomic_load(&counter)); return 0; } ``` Run it: you will get exactly 2000000 every time. Now change `atomic_fetch_add_explicit` to a plain `counter++` (a non-atomic read-modify-write), recompile without the atomic header, and run it a few times. You will get different results below 2000000 each time — that is the data race. The atomic version compiles to a single `lock xadd` instruction on x86, which is why it works without a mutex.