The naive boolean negation
std::atomic_bool b;
b = !b;
does not seem to be atomic. I suspect this is because operator!
triggers a
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:
Instead of atomic
, use an integral type (e.g. atomic
or atomic
) which can be 0 or 1, and xor it with 1:
std::atomic flag(0);
flag ^= 1; //equivalent to flag.fetch_xor(1);
Unfortunately, fetch_xor
is not provided on atomic
, only on integral types.
Perform a compare/exchange operation in a loop, until it succeeds:
std::atomic 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.