How to atomically negate an std::atomic_bool?

后端 未结 1 873
-上瘾入骨i
-上瘾入骨i 2021-01-31 08:15

The naive boolean negation

std::atomic_bool b;
b = !b;

does not seem to be atomic. I suspect this is because operator! triggers a

1条回答
  •  遥遥无期
    2021-01-31 08:32

    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, 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.

    2. 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.

    0 讨论(0)
提交回复
热议问题