volatile vs atomics — difference and when each is needed - MarekBykowski/readme GitHub Wiki

// volatile — prevents compiler caching, does NOT guarantee atomicity
volatile uint32_t *reg = (volatile uint32_t *)0x40011004;
while ((*reg & 0x01) == 0);   // compiler must re-read every iteration

// C11 atomics — guarantee both visibility AND atomicity
#include <stdatomic.h>
atomic_int shared = ATOMIC_VAR_INIT(0);
atomic_fetch_add(&shared, 1);                         // thread-safe increment
int v = atomic_load_explicit(&shared, memory_order_acquire);
volatile atomic
Prevents compiler caching
Prevents CPU reordering
Atomic read-modify-write
Use for HW registers
Use for shared variables (threads/ISR) ✗ alone
// Classic ISR pattern — volatile is correct here (single read/write, no RMW)
volatile bool flag = false;

void ISR_handler(void) {
    flag = true;   // single store — atomic on most architectures
}

void main_loop(void) {
    while (!flag);   // volatile ensures re-read every time
    flag = false;
    process();
}

⚠️ volatile is NOT a substitute for atomics in multi-threaded code. On multi-core ARM, you need memory barriers too.