CodeNebula
C++11coreintermediate

std::atomic (C++11 Addition)

C++11 · Modern Revolution · Compiler flag: -std=c++11

Explanation

Provides lock-free atomic operations on fundamental types, enabling safe concurrent access without mutexes for simple shared variables.

Real-World Scenario

Implement a thread-safe reference counter or a shared flag indicating shutdown in a multi-threaded application.

Quick Snippet

snippet.cpp
#include <atomic>
std::atomic<int> counter(0);

void increment() {
    counter.fetch_add(1, std::memory_order_relaxed);
}

Full Example

atomic.cpp
// CodeNebula - C++11 Example: std::atomic
// Compile: g++ -std=c++11 -Wall -Wextra -pthread -o example atomic.cpp
//
// This example demonstrates:
// - std::atomic for lock-free thread-safe operations
// - load(), store(), exchange(), compare_exchange
// - Atomic arithmetic: fetch_add, fetch_sub, ++, --
// - std::atomic_flag as a spinlock
// - Comparison with non-atomic operations

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>

// ============================================================
// 1. Basic atomic operations
// ============================================================
std::atomic<int> atomicCounter(0);
int regularCounter = 0;

void incrementAtomic(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        atomicCounter.fetch_add(1);  // Atomic increment
    }
}

void incrementRegular(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        ++regularCounter;  // Data race!
    }
}

// ============================================================
// 2. Atomic load, store, exchange
// ============================================================
std::atomic<bool> ready(false);
std::atomic<int> payload(0);

void producer() {
    payload.store(42);           // Store value
    ready.store(true);           // Signal ready
}

void consumer() {
    while (!ready.load()) {}     // Spin until ready
    int val = payload.load();    // Read value
    std::cout << "  Consumer got: " << val << std::endl;
}

// ============================================================
// 3. compare_exchange (CAS operation)
// ============================================================
std::atomic<int> casValue(100);

bool tryUpdate(int expected, int desired) {
    // Only updates if current value equals expected
    return casValue.compare_exchange_strong(expected, desired);
}

// ============================================================
// 4. atomic_flag spinlock
// ============================================================
class SpinLock {
    std::atomic_flag flag_ = ATOMIC_FLAG_INIT;
public:
    void lock() {
        while (flag_.test_and_set(std::memory_order_acquire)) {
            // Spin until we acquire the lock
        }
    }

    void unlock() {
        flag_.clear(std::memory_order_release);
    }
};

SpinLock spinlock;
int protectedData = 0;

void spinlockWorker(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        spinlock.lock();
        ++protectedData;
        spinlock.unlock();
    }
}

int main() {
    std::cout << "=== std::atomic ===" << std::endl;

    // Atomic vs regular counter
    std::cout << "\n--- Atomic vs regular counter ---" << std::endl;
    {
        std::vector<std::thread> threads;
        for (int i = 0; i < 4; ++i) {
            threads.emplace_back(incrementAtomic, 10000);
            threads.emplace_back(incrementRegular, 10000);
        }
        for (auto& t : threads) t.join();

        std::cout << "  Atomic counter:  " << atomicCounter.load()
                  << " (expected: 40000)" << std::endl;
        std::cout << "  Regular counter: " << regularCounter
                  << " (may be wrong due to race)" << std::endl;
    }

    // Load, store, exchange
    std::cout << "\n--- load/store/exchange ---" << std::endl;
    std::thread prod(producer);
    std::thread cons(consumer);
    prod.join();
    cons.join();

    std::atomic<int> val(10);
    int old = val.exchange(20);  // Atomically set to 20, return old
    std::cout << "  exchange: old=" << old << ", new=" << val.load() << std::endl;

    // CAS
    std::cout << "\n--- compare_exchange ---" << std::endl;
    std::cout << "  Current value: " << casValue.load() << std::endl;
    bool success1 = tryUpdate(100, 200);
    std::cout << "  CAS(100->200): " << (success1 ? "success" : "failed")
              << ", value=" << casValue.load() << std::endl;
    bool success2 = tryUpdate(100, 300);  // Fails: value is now 200
    std::cout << "  CAS(100->300): " << (success2 ? "success" : "failed")
              << ", value=" << casValue.load() << std::endl;

    // Atomic arithmetic
    std::cout << "\n--- Atomic arithmetic ---" << std::endl;
    std::atomic<int> num(10);
    std::cout << "  Initial: " << num.load() << std::endl;
    num.fetch_add(5);
    std::cout << "  After fetch_add(5): " << num.load() << std::endl;
    num.fetch_sub(3);
    std::cout << "  After fetch_sub(3): " << num.load() << std::endl;
    ++num;
    std::cout << "  After ++: " << num.load() << std::endl;

    // Spinlock
    std::cout << "\n--- atomic_flag spinlock ---" << std::endl;
    {
        std::vector<std::thread> threads;
        for (int i = 0; i < 4; ++i) {
            threads.emplace_back(spinlockWorker, 10000);
        }
        for (auto& t : threads) t.join();
        std::cout << "  Protected data: " << protectedData
                  << " (expected: 40000)" << std::endl;
    }

    // Properties
    std::cout << "\n--- Atomic properties ---" << std::endl;
    std::cout << "  atomic<int> is lock-free: "
              << atomicCounter.is_lock_free() << std::endl;

    return 0;
}

// Expected output (regular counter may vary due to race):
// === std::atomic ===
//
// --- Atomic vs regular counter ---
//   Atomic counter:  40000 (expected: 40000)
//   Regular counter: 38756 (may be wrong due to race)
//
// --- load/store/exchange ---
//   Consumer got: 42
//   exchange: old=10, new=20
//
// --- compare_exchange ---
//   Current value: 100
//   CAS(100->200): success, value=200
//   CAS(100->300): failed, value=200
//
// --- Atomic arithmetic ---
//   Initial: 10
//   After fetch_add(5): 15
//   After fetch_sub(3): 12
//   After ++: 13
//
// --- atomic_flag spinlock ---
//   Protected data: 40000 (expected: 40000)
//
// --- Atomic properties ---
//   atomic<int> is lock-free: 1
Run command
g++ -std=c++11 -Wall -Wextra -pthread -o example atomic.cpp