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();
}
⚠️
volatileis NOT a substitute for atomics in multi-threaded code. On multi-core ARM, you need memory barriers too.