How to atomically negate an std::atomic_bool?

b = !b is not atomic because in the C++ source you have an atomic pure read of b (equivalent to b.load(), and then a separately-atomic assignment to b (equivalent to b.store()).

Nothing makes the entire combination into an atomic RMW operation in the C++ abstract machine, and there’s no syntax for composing arbitrary operations into atomic RMW operations (other than putting it into a CAS retry loop).


There are two options to use:

  1. Instead of atomic<bool>, use an integral type (e.g. atomic<int> or atomic<unsigned char>) which can be 0 or 1, and xor it with 1:

    std::atomic<int> flag(0);
    
    flag ^= 1;        //equivalent to flag.fetch_xor(1);
    

    Unfortunately, fetch_xor is not provided on atomic<bool>, only on integral types.

  2. Perform a compare/exchange operation in a loop, until it succeeds:

    std::atomic<bool> flag(false);
    
    bool oldValue = flag.load();
    while (!flag.compare_exchange_weak(oldValue, !oldValue)) {}
    

    Unfortunately compilers for x86 won’t typically optimize this loop into
    lock xor byte [flag], 1 in the asm; you’ll get an actual cmpxchg retry loop. In practice cmpxchg retry loops are fine with low contention. In the worst case this is not wait-free, but is lock-free because at least one thread will make progress every time they all retry. (In practice it’s more complicated with hardware arbitration for which core even gets access to the cache line to make an attempt.)

    If high contention is possible, prefer the integer version that lets you use an atomic xor.

Leave a Comment