I have the following C++ 2011 code:
std::atomic<bool> x, y;
std::atomic<int> z;
void f() {
x.store(true, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
y.store(true, std::memory_order_relaxed);
}
void g() {
while (!y.load(std::memory_order_relaxed)) {}
std::atomic_thread_fence(std::memory_order_acquire);
if (x.load(std::memory_order_relaxed)) ++z;
}
int main() {
x = false;
y = false;
z = 0;
std::thread t1(f);
std::thread t2(g);
t1.join();
t2.join();
assert(z.load() !=0);
return 0;
}
At my computer architecture class, we've been told that the assert in this code always comes true. But after reviewing it thouroughly now, I can't really understand why it's so.
For what I know:
- A fence with 'memory_order_release' will not allow previous stores to be executed after it
- A fence with 'memory_order_acquire' will not allow that any load that comes after it to be executed before it.
If my understanding is correct, why can't the following sequence of actions occur?
- Inside t1,
y.store(true, std::memory_order_relaxed);
is called - t2 runs entirely, and will see a 'false' when loading 'x', therefore not increasing z in a unit
- t1 finishes execution
- In the main thread, the assert fails because z.load() returns 0
I think this complies with 'acquire'-'release' rules, but, for example in the best answer in this question: Understanding c++11 memory fences which is very similar to my case, it hints that something like step 1 in my sequence of actions cannot happen before the 'memory_order_release', but doesn't get into details for the reason behind it.
I'm terribly puzzled about this, and will be very glad if anyone could shed some light on it :)