0
#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 1 has not happened, 3 and 4 will never happen, so we can assume that 1 happens before 3. We have 1->3 and 1->4;
  • In threads t1 and t2, we have 1->2 and 3->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.

13
  • 4
    A seq_cst store can reorder with a later relaxed store in practice on AArch64 (stlr is a release store plus has special interaction with ldar seq_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. Commented Jul 1 at 15:01
  • 2
    If 2-1 reordering 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't release, and the first store being release doesn't stop StoreStore reodering: preshing.com/20120913/acquire-and-release-semantics Commented Jul 1 at 15:16
  • @PeterCordes It's my guess that a relaxed operation will be released when reordering it before a seq-cst operation, it seems my guess is incorrect. I deduced it from release-acquire model. Commented Jul 2 at 11:33
  • What do you mean "will be released"? If the compiler statically reordered the store instructions, then the 2-1 ordering would be the only one possible, since 1 is 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. Commented Jul 2 at 11:46
  • 2
    Oh, you're talking about the total order of SC operations? 2 isn't part of that because it's not memory_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. ... Commented Jul 2 at 14:40

2 Answers 2

2

In comments, you clarified that by "global sequence" you mean the total order S defined in atomics.order p4. This is clearly defined as an order on the seq_cst operations only. Your relaxed store #2 is not an element of this order at all. So the only possible ordering for S is 1 -> 3 -> 4.

a relaxed-store is able to be shown in a global sequence only if some load-operations sees the value

There is no such rule. A relaxed load or store will never appear in the global total order S, by its very definition.

The assert can certainly fail. In fact, it can fail even if you strengthen #2 to be seq_cst. In that case, 1 -> 3 -> 4 -> 2 is a perfectly valid ordering for S, because there is no rule that forbids it. It does not contain a "ring" (the more usual term is "cycle") because 2 does not have to precede any of the other operations - there is no rule which would require that it does.


I asked ChatGPT.

That's not typically helpful with questions like this. Showing your research effort in your question is encouraged, but asking ChatGPT isn't very productive, and telling us what it said does not contribute much of value to the question. Tell us what you tried in your efforts to solve the problem, not what a machine tried.

See also How can I handle questions which provide an MCVE or "what I tried" generated by ChatGPT?

ChatGPT gave me a rigorous analysis

No, it gave you nonsense that looks like a rigorous analysis.

but the answer confused me.

Because it is nonsense.

Sign up to request clarification or add additional context in comments.

Comments

1

Because if 2 is reordered and it happens before 1

Since 1 is sequenced-before 2, 1 happens-before 2 as well. That can't be reversed.

the relaxed-store 2 will be synchronized while operation 1 released

Not sure what this means, relaxed operations can't participate in synchronization.

So there are three global sequences left

It makes some sense informally, but formally it sounds wrong to me. Happens-before isn't necessarily a total order. That is, for some pairs of operations, it can be the case that neither happens before the other.

E.g. 2 and 3 seem to not happen-before each other, because nothing forces them to.

Also 2 and 4 don't happen-before each other. Even if 4 reads the value written by 2, since at least one of the operations is relaxed, there's no synchronization and no happens-before.

Therefore the whole talk about "global sequence" is a bit moot. Maybe you mean the seq-cst order by that, but then 2 doesn't participate in that order (as Peter Cordes said in the comments), and the seq-cst order can even be inconsistent with happens-before in rare cases, and doesn't imply happens-before.

S3 is not a valid global sequence

I don't see why.

Even ignoring the fancy memory orders, if the first thread happens to wait long enough between 1 and 2, this can happen.

I asked ChatGPT.

None of what it said makes any sense, because "global sequence" isn't a thing, and because it didn't explain what O1 and O2 are, and also because:

Order O2 : 1->2
... 2 comes before 4 in O2

...there's clearly no 4 in O2, whatever O2 is.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.