C stdatomic.h (C11)
Thread-safe atomic operations in modern C
⚛️ What is stdatomic.h?
The stdatomic.h header provides atomic operations for thread-safe programming. Atomic operations prevent data races when multiple threads access shared variables simultaneously, ensuring program correctness.
#include <stdatomic.h>
#include <stdio.h>
atomic_int counter = 0;
int main() {
atomic_fetch_add(&counter, 5);
printf("Counter: %d\n", counter);
return 0;
}
Atomic Types
atomic_int
Thread-safe integer operations
atomic_int count = 0;
atomic_bool
Thread-safe boolean values
atomic_bool flag = false;
atomic_ptr
Thread-safe pointer operations
atomic_int_ptr ptr;
atomic_size_t
Thread-safe size operations
atomic_size_t size = 0;
🔹 Basic Atomic Operations
Atomic operations in C provide thread-safe ways to manipulate shared data without requiring explicit mutex locks for simple operations. The <stdatomic.h> header offers atomic types and functions that guarantee operations complete as indivisible units, preventing race conditions in concurrent programming. Common atomic operations include atomic_load(), atomic_store(), atomic_fetch_add(), and atomic_fetch_sub(). For example, atomic_int counter; atomic_fetch_add(&counter, 1); safely increments a shared counter from multiple threads. These operations are essential for building lock-free data structures, implementing efficient synchronization primitives, and optimizing performance in multi-threaded applications where traditional locking would create bottlenecks.
#include <stdatomic.h>
#include <stdio.h>
int main() {
atomic_int value = 10;
// Atomic load
int current = atomic_load(&value);
printf("Current: %d\n", current);
// Atomic store
atomic_store(&value, 20);
// Atomic add
int old = atomic_fetch_add(&value, 5);
printf("Old: %d, New: %d\n", old, atomic_load(&value));
return 0;
}
Output:
Current: 10 Old: 20, New: 25
🔹 Compare and Swap
The compare-and-swap (CAS) operation is a fundamental atomic operation that forms the basis for many lock-free algorithms and concurrent data structures. Using atomic_compare_exchange_strong() or atomic_compare_exchange_weak(), programmers can atomically check if a memory location contains an expected value and update it only if the comparison succeeds. For example, atomic_compare_exchange_strong(&variable, &expected, desired) compares variable with expected and updates it to desired if they match. This operation is crucial for implementing lock-free queues, stacks, and other concurrent data structures where multiple threads need to modify shared state without traditional locking mechanisms that could cause contention and performance degradation.
#include <stdatomic.h>
#include <stdio.h>
int main() {
atomic_int value = 100;
int expected = 100;
int desired = 200;
// Compare and swap
bool success = atomic_compare_exchange_strong(&value, &expected, desired);
if (success) {
printf("Swap successful! New value: %d\n", atomic_load(&value));
} else {
printf("Swap failed. Expected: %d, Actual: %d\n", expected, atomic_load(&value));
}
return 0;
}
Output:
Swap successful! New value: 200
🔹 Memory Ordering
Memory ordering constraints control how atomic operations are reordered by compilers and processors, ensuring correct synchronization in multi-threaded programs. C provides several memory order options including memory_order_relaxed, memory_order_acquire, memory_order_release, memory_order_seq_cst, and others. For instance, atomic_store_explicit(&flag, 1, memory_order_release) ensures all previous writes are visible before this store operation completes. Understanding memory ordering is critical for writing efficient concurrent code, as it allows developers to balance performance with correctness. Sequential consistency provides the strongest guarantees but may limit optimization opportunities, while relaxed ordering offers maximum performance at the cost of requiring careful reasoning about memory visibility.
Memory Order Types:
- memory_order_relaxed: No ordering constraints
- memory_order_acquire: Acquire semantics
- memory_order_release: Release semantics
- memory_order_seq_cst: Sequential consistency (default)
#include <stdatomic.h>
atomic_int data = 0;
atomic_bool ready = false;
void producer() {
atomic_store(&data, 42);
atomic_store_explicit(&ready, true, memory_order_release);
}
void consumer() {
while (!atomic_load_explicit(&ready, memory_order_acquire)) {
// Wait for data to be ready
}
int value = atomic_load(&data);
printf("Received: %d\n", value);
}