I know that the connection between synchronization and communication will occur between the release store operation in stream 2 and the load load operation in stream 1, even if this load operation does not directly read the value stored in stream 2, provided that there is a “release sequence” between a release storage operation and a repository that is actually read if:
- The store that is actually being read is in the same thread as the store operation.
- By modification order, there is no storage in other threads between the release store operation and the store that is actually being read (although read-modify-write operations are allowed).
However, I see no reason why it would not be possible to synchronize when the repository that is actually being read is in a different thread, provided that the release repository operation is still happening - until which it is actually being read. Is this an explicitly forbidden standard? If so, is it not possible that the standard is incomplete, because it makes sense, and all existing equipment will have such synchronization in any case?
Consider the following example, where a, x, and y are atomic ints initialized to 0.
Theme 1:
k = y.load(memory_order_acquire); x.store(1, memory_order_relaxed);
Theme 2:
m = x.load(memory_order_relaxed); y.store(2, memory_order_release); a.store(2, memory_order_release);
Theme 3:
n = a.load(memory_order_acquire); y.store(3, memory_order_relaxed);
where is the question: is it possible that we will end with k = 3, m = 1 and n = 2?
If there is no storage and y in stream 3 between the store and y in stream 2, then there is no synchronization between the release store and y in stream 2 and reading y in stream 1, and therefore there is no need for loading x in stream 2 to go to the store before x in stream 1, which made possible the desired result k, m and n.
But, if there is y in stream 3 between the store and y in stream 2 and the store, there is synchronization between the release store and y in stream 2 and reading y in stream 1 and, therefore, loading x in stream 2 must be performed - before storing to x in stream 1, which makes the desired result k, m and n impossible. Note that if there was no storage / loading a, and we just made a relaxed store of values 3 to y at the end of stream 2, then it will be so (so it never happens that k = 3 and m = 1).
In this case, the value 3 is stored in y in thread 3, but there is synchronization with release-release using the atomic variable a; therefore, if n = 2, then there is a relationship between the release store of value 2 and y and the weakened store of value 3 to y. Does this not mean that there is a release sequence and a result when k = 3, m = 1 and n = 2 will never happen?
Edit
Note that the following code snippet is executed:
int main() { atomic_int a = 0; atomic_int x = 0; atomic_int y = 0; {{{ { y.load(memory_order_acquire).readsvalue(3); x.store(1, memory_order_relaxed); } ||| { x.load(memory_order_relaxed).readsvalue(1); y.store(2, memory_order_release); a.store(2, memory_order_release); } ||| { a.load(memory_order_acquire).readsvalue(2); y.store(3, memory_order_relaxed); } }}} }
on http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/ leads to 1 sequential execution:

The reason is that there is no rs edge from node g to node j (and therefore there is no sw / hb edge from j to d).
For comparison, when we put a relaxed record simply at the end of stream 2:
int main() { atomic_int a = 0; atomic_int x = 0; atomic_int y = 0; {{{ { y.load(memory_order_acquire).readsvalue(3); x.store(1, memory_order_relaxed); } ||| { x.load(memory_order_relaxed).readsvalue(1); y.store(2, memory_order_release); y.store(3, memory_order_relaxed); } }}} }
Then there is no sequential execution, that is:

violates causality if node f reads from node e and f reads to node e. The main difference here is that there is now an “rs” edge from node g to h, which causes synchronization with the (sw) border from node g to node d and therefore occurs before (hb) between the same nodes.