Is it possible for the sequence of instances from the storage operation to be stored in another thread?

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:

enter image description here

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:

enter image description here

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.

+6
source share
1 answer

The y store in stream 3 does not refer to the release sequence that is stored in the store, until y in stream 2 in accordance with the rules that determine the release sequence, so as your analysis, your desired result is possible.

Then I will try to explain why the release sequence should contain operations in one thread (except read-modify-write operations) using your example. For convenience, add a few labels for these operations:

Theme 1:

 k = y.load(memory_order_acquire); // #1 x.store(1, memory_order_relaxed); // #2 

Theme 2:

 m = x.load(memory_order_relaxed); // #3 y.store(2, memory_order_release); // #4 a.store(2, memory_order_release); // #5 

Theme 3:

 n = a.load(memory_order_acquire); // #6 y.store(3, memory_order_relaxed); // #7 

In your example, the optimizer can reorder # 6 and # 7 according to the relaxed ordering property. Please note that even after this reordering, the program still behaves as if # 3 - # 7 maintains the “occurs before” relationship, because

  • behavior # 7 is independent of other side effects, and

  • in stream 3, then read y can be interpreted as a direct read on # 7, that is, return access to the actual atomic object if there is no synchronization operation after # 7.

Thus, such optimization will be prohibited if the rule allows # 7 to be in the release sequence led by # 4.

0
source

Source: https://habr.com/ru/post/1274743/


All Articles