According to cppreference, we can use release-acquire semantics to ensure write-read precedence between two threads as follows:

// Thread A
1. Write to X
2. Atomic store to Y with std::memory_order_release
// Thread B
3. Atomic load at Y with std::memory_order_acquire
4. Read at X

Supposing that 2 is ordered before 3 (take it as a given that this occurs according to Y), we are guaranteed that A's write to X is visible to B since we can establish a total order on the events (as conveniently described by my enumeration above) because 1 sequenced-before 2 synchronizes-with 3 sequenced-before 4 and because release-acquire ensure that all of A's writes are visible to B after 3.

However, the link above explicitly states that in this situation "all memory writes (non-atomic and relaxed atomic) [by A become] visible side-effects in thread B." My question is then: are "visible side-effects" considered writes made B to threads previously unaware of A's writes?

In other words, suppose I had a third thread C and another atomic:

// Thread A
1. Write to X
2. Atomic store to Y with std::memory_order_release
// Thread B
3. Atomic load at Y with std::memory_order_acquire
4. Read at X
5. Atomic store to Z with std::memory_order_release
// Thread C
6. Atomic load at Z with std::memory_order_acquire
7. Read at X

(Assume similarly that 5 happens-before 6). Would C read A's write to X? Or is B's "visible side-effect" on X not considered a write?


Formally, you are asking if the side effect on X in thread A is visible with respect to value computation of X in thread C.

For that to be true, 1 needs to happen-before 7.

A happens-before B is defined, as synchronizes-with or inter-thread happens-before, and the latter is defined as

for some evaluation X,

— A synchronizes with X and X is sequenced before B, or

— A is sequenced before X and X inter-thread happens before B, or

— A inter-thread happens before X and X inter-thread happens before B.

So yes, it is perfectly transitive across multiple threads. In particular, in your case,

1 sequenced-before 2

2 synchronizes-with 3

3 sequenced-before 5

5 synchronizes-with 6

6 sequenced-before 7

Therefore, 1 happens-before 7, therefore 1 is a visible side-effect with respect to 7, and therefore the value of non-atomic scalar object X as determined by the value computation of X (your "Read at X") is guaranteed to be the value stored by the visible side-effect 1.

  • Ah, so according to your second sentence, it is sufficient for a write on one thread to happen-before a read on another thread by any chain of precedence for the value to be seen. Would you mind citing that claim, if I understood you correctly? – VF1 Aug 17 '14 at 7:15
  • And, if you don't mind, since you heavily edited this page regarding atomic_... overloads for shared_ptr, could you please tell me if you know of any compilers that actually support this feature? Many claim full C++11 support, but this is always undefined (I tried clang 3.4, gcc 4.8, and a few online compilers)... – VF1 Aug 17 '14 at 7:22
  • @VF1 the citation is the one I quoted. And clang has had the shared_ptr atomics since 2011, coliru.stacked-crooked.com/a/613b848af2d3b571 (note that they are part of the library, you probably used clang with GNU library, which of course is not anywhere near C++11) – Cubbi Aug 17 '14 at 7:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.