The definitions mean that none of the memory operation in A can be reordered after the store to a
(definition of release in the question). And that none of the operations in B or C can precede the load of a
in the other thread (definition of acquire in the question).
Now, the example is assuming that there is no other code that operates on a
, or at least that writes to a
. If B is executed, it means that a
had the value 1 when the load into tmp
happened. For a
to have value 1, the fetch_add
had to be executed before (the fetch_add
synchronizes with the load
), which means that the code in A completed execution. If that particular code path is hit, the ordering of the operations is:
A
a.fetch_add(1,memory_order_release);
int tmp = a.load(memory_order_acquire);
B
Now the other options is that when the load
happens the value is still 0. That implies that the first thread did not get to execute the fetch_add
, but there is no guarantee as of where it might be executing. By the time that thread 2 executes C, A might still be executing (or it might have not even started, or it might have completed).
If both A and B access the same variable, there is no conflict, because the atomic guarantees that B can only be executed after A has already completed. On the other hand, that guarantee is not given for C, so if A and C touch the same variable and at least one of them writes to it, then there is a race condition.