I've been reading about C++ memory models and think I have a grasp of the basic concepts of happens-before, synchronizes-with and inter-thread happens-before. However, applying them in some examples has been difficult. In particular I have been thinking about the following sequence of operations when they are repeated:

``````Thread 1
1. Atomic store of X in A (seq cst).
2. Write X to B.
3. Atomic store of X in C (seq cst).

``````

I think the key thing is whether the atomic store in 1 implies anything about the store in 2? If 2 had been done before 1 then it does but that's not the case here. If the result of 2 is seen by Thread 2 before it sees the results of 1 and 3 then Thread 2 would see A and C set to X but B set to Y. Is this possible? How would that be described given the concepts above. Finally, is some other factors coming into play that may mean the program I wrote does work (on x86)?

I know that following 4 Thread 2 must see the write made by Thread 1 in 2

First example may work:

``````      Thread 1                    Thread 2

1. atomic store in A:  X
2. write in B : X
3. atomic store in C : X
4. atomic load of C  -> X
6. atomic load of A -> X
``````

Second example won't:

``````      Thread 1                    Thread 2
initially A,B and C hold Z

1. atomic store in A:  X
4. atomic load of C  -> Z
2. write in B : X           5. read B  -> ?? (UB: can b Z, can be X, can be a mix or anything else
3. atomic store in C : X
6. atomic load of A -> X
``````

Two problems happen in this example, first the load of C can occur before the write, causing the previous value to be fetched. Second, and worse, the access of B is not guaranteed to be atomic and not protected against a race: so you might experience UB.

what happens if Thread 1 is spinning and after 3 is setting A then B and then C to Y ?

Let's look at a potential scenario:

``````      Thread 1                    Thread 2

1. atomic store in A:  X
2. write in B : X
3. atomic store in C : X
4. atomic load of C  -> X
n. atomic store in A: Y     5. read B  -> X
n+1. write in B: Y          6. atomic load of A -> Y
n+2. atomic sore in C : Y
``````

Again, you might not have what you've expected. Here I get Y in A but I could have got X. I could get X in B, but I could as well have got Y or even an unexpected value in case of a race.

Conclusion

The happen-before relation within a single thread is straight forward and follows the dermined/predictible flow of control.

As soon as you have a synchronization between two threads (here the reading of atomic), the "inter-thread happen-before" is not predetermined. It depends on the point fo time of the synchronization, and must be analyzed by looking at the relative happen-before in each thread, not forgetting that immediately after other things may still change.

Finally, avoid non-atomic shared variables, unless you protect them with a lock to prevent data races.