Search code examples
c++memory-barrierslock-freestdatomicspinlock

How does atomic seq_cst memory order actually work?


For example, there are shared variables.

int val;
Cls obj;

An atomic bool variable acts as a data indicator.

std::atomic_bool flag = false;

Thread 1 only set these variables.

while (flag == true) { /* Sleep */ }

val = ...;
obj = ...;

flag = true; /* Set flag to true after setting shared variables. */

Thread 2 only get these variables.

while (flag != true) { /* Sleep */ }

int local_val = val;
Cls local_obj = obj;

flag = false; /* Set flag to false after using shared variables. */

My questions are:

  1. For std::memory_order_seq_cst, which is default for std::atomic_bool, is it safe to set or get shared variables after while (...) {}?

  2. Using bool instead of std::atomic_bool is correct or not?


Solution

    1. Yes, the code is fine, and as ALX23z says, it would still be fine if all the loads of flag were std::memory_order_acquire and all the stores were std::memory_order_release. The extra semantics that std::memory_order_seq_cst provides are only relevant to observing the ordering between loads and stores to two or more different atomic variables. When your whole program only has one atomic variable, it has no useful effect.

      Roughly, the idea is that acquire/release suffice to ensure that the accesses to your non-atomic variables really do happen in between the load/store of the atomic flag, and do not "leak out". More formally, you could use the C++11 memory model axioms to prove that given any read of the objects in thread 1, and any write of the objects in thread 2, one of them happens before the other. The details are a little tedious, but the key idea is that an acquire load and a release store is exactly what you need to get synchronization, which is how you get a happens-before relation between operations in two different threads.

    2. No, if you replace the atomic_bool with a plain bool then your code has undefined behavior. You would then be reading and writing all your variables in different threads, with no mutexes or atomic variables that could possibly create synchronization, and that is the definition of a data race.