C Atomic Operations (<stdatomic.h>)

Thread-safe operations without locks

⚛️ What is stdatomic.h?

The stdatomic.h header provides atomic operations for lock-free programming. Atomic operations are indivisible and thread-safe, allowing concurrent access to shared variables without explicit synchronization mechanisms like mutexes.


#include <stdatomic.h>
#include <stdio.h>

atomic_int counter = 0;
atomic_fetch_add(&counter, 1);
                                    

Key Atomic Concepts

🔢

Atomic Types

Thread-safe variable types

atomic_int count;
atomic_bool flag;
atomic_long value;
📖

Atomic Load

Safely read atomic variables

int val = atomic_load(&counter);
printf("Value: %d\n", val);
💾

Atomic Store

Safely write to atomic variables

atomic_store(&counter, 42);
// Thread-safe assignment

Atomic Operations

Perform atomic arithmetic

atomic_fetch_add(&counter, 5);
atomic_fetch_sub(&counter, 2);

🔹 Basic Atomic Counter

Atomic operations ensure thread-safe counter updates in multithreaded applications without explicit locking mechanisms or synchronization primitives. Use atomic_int and operations like atomic_fetch_add() for lock-free concurrent access. Atomic operations from stdatomic.h provide guarantees that operations complete without interruption. Multiple threads can safely increment counters simultaneously without data races or corruption. Atomic operations are fundamental for high-performance concurrent programming where performance and correctness must coexist.

#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>

atomic_int global_counter = 0;

int worker_thread(void* arg) {
    for (int i = 0; i < 1000; i++) {
        // Atomic increment - no mutex needed!
        atomic_fetch_add(&global_counter, 1);
    }
    return 0;
}

int main() {
    thrd_t threads[3];
    
    // Create 3 threads
    for (int i = 0; i < 3; i++) {
        thrd_create(&threads[i], worker_thread, NULL);
    }
    
    // Wait for all threads
    for (int i = 0; i < 3; i++) {
        thrd_join(threads[i], NULL);
    }
    
    printf("Final counter: %d\n", atomic_load(&global_counter));
    return 0;
}

Output:

Final counter: 3000

🔹 Compare and Swap

Atomic compare-and-swap operations are essential for building lock-free algorithms in concurrent programming. The atomic_compare_exchange function checks if a variable holds an expected value and atomically replaces it with a new value if the comparison succeeds. This operation is fundamental for thread-safe data structures because it prevents race conditions without using expensive locks. For example, atomic_compare_exchange_strong(&value, &expected, 20) will change the value to 20 only if it currently equals the expected value, returning true on success. This technique enables high-performance concurrent algorithms that scale efficiently across multiple processor cores.

#include <stdatomic.h>
#include <stdio.h>

atomic_int shared_value = 10;

int main() {
    int expected = 10;
    int desired = 20;
    
    // Try to change value from 10 to 20
    if (atomic_compare_exchange_strong(&shared_value, &expected, desired)) {
        printf("Successfully changed %d to %d\n", expected, desired);
    } else {
        printf("Failed to change, current value: %d\n", expected);
    }
    
    printf("Current value: %d\n", atomic_load(&shared_value));
    return 0;
}

Output:

Successfully changed 10 to 20
Current value: 20

🧠 Test Your Knowledge

What is the main advantage of atomic operations?