#include <atomic>
#include <thread>
#include <cassert>
#include <chrono>
std::atomic<int> x {}, y {};
void write_x_and_y() {
x.store(1, std::memory_order_seq_cst); // 1
y.store(-1, std::memory_order_relaxed); // 2
}
void read_x_then_y() {
while(x.load(std::memory_order_seq_cst) not_eq 1); // 3
assert(y.load(std::memory_order_seq_cst) < 0); // 4
}
int main(int argc, char *argv[]) {
A: std::thread t1(write_x_and_y), t2(read_x_then_y);
t1.join();
t2.join();
goto A;
}
First, reordering is never considered. Because if 2 is reordered and it happens before 1, the relaxed-store 2 will be synchronized while operation 1 released. We can see that (A happens before B will be marked as A->B)
- If
1has not happened,3and4will never happen, so we can assume that1happens before3. We have1->3and1->4; - In threads
t1andt2, we have1->2and3->4.
So there are three global sequences left :
- Sequence S1 :
1->2->3->4; - Sequence S2 :
1->3->2->4; - Sequence S3:
1->3->4->2.
In S1 and S2, assertion in t2 is always correct. S3 is not a valid global sequence. My opinion is very simple, a relaxed-store is able to be shown in a global sequence only if some load-operations sees the value. In order S3, no one sees the result of y.store(-1), so 2 cannot be shown in the global sequence.
I asked ChatGPT. ChatGPT gave me a rigorous analysis, but the answer confused me.
ChatGPT thinks no rings can appear in a global sequence. But I cannot find a ring in 1->3->4->2, maybe ChatGPT means
|--------|
1->3->4->2
In this graph, it's impossible to go back to the starting node when we traversal from the starting node. And then ChatGPT said the definition of cycle in global sequence is different from the definition in Graph Theory. Finally, ChatGPT said you can list all orders and align them, if you find
Order O1 : 1->3->4->2
Order O2 : 1->2
2 comes after 4 in O1 but 2 comes before 4 in O2,
the sequence 1->3->4->2 is invalid??? I always feel that this method is very arbitrary and lacks rigorous basis.
stlris a release store plus has special interaction withldarseq_cst loads, but not other later ops.) So the assert can fail. IDK if that invalidates any of your other reasoning about other orders. I wouldn't trust ChatGPT at all for reasoning about this; it's terrible at computer architecture questions about explaining performance effects, and probably also at complex reasoning about memory orders like this.2-1reordering happens, the assert can fail. I don't understand your first sentence; are you trying to give a reason why that reordering can't happen? It can; SC ordering only applies when both operations are SC, like in the reader thread. The second store isn'trelease, and the first store being release doesn't stop StoreStore reodering: preshing.com/20120913/acquire-and-release-semantics1is a release operation preventing runtime reordering. But with just runtime reordering, either store could be the first one that becomes visible to the reader thread.2isn't part of that because it's notmemory_order_seq_cst. But more importantly, no, stores don't have to be seen by loads. Nothing forbids the last operation in a program from being a store. The actual rule in the ISO standard is eel.is/c++draft/atomics#order-4 which doesn't talk about ops being observed, just relative orders. There is a single total order S on all memory_order::seq_cst operations, including fences, that satisfies the following constraints. ...