Relaxed ordering
Atomic operations tagged with std::memory_order_relaxed are not synchronization operations; only the modification order of each individual atomic object is shared between threads. Different objects have no ordering between themselves relative to other threads; operations can be seen out of order.
Example – Relaxed ordering
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<int> x{ 0 };
std::atomic<bool> x_is_set{ false };
std::atomic<int> counter{ 0 };
void f1()
{
x.store(5, std::memory_order_relaxed); // A
x_is_set.store(true, std::memory_order_relaxed); // B
}
void f2()
{
while (!x_is_set.load(std::memory_order_relaxed)); // C
if (x.load(std::memory_order_relaxed) == 5) // D
++counter; // E
}
int main()
{
std::thread t1{ f1 };
std::thread t2{ f2 };
t1.join();
t2.join();
assert(counter.load() == 1); // F
}
There are no ordering constraints between thread t1 and thread t2. Therefore, the store done in B can be seen by t2 before the store done by A; the assert F in main() will fire in that case.
The obvious solution to this issue is to make the store in B have std::memory_order_release
and the load in C have std::memory_order_acquire
for synchronization purposes. The assert in F would seemingly never fire.
Question
However, since there is no happens-before relationship between A and B (am I wrong?), can't the compiler/optimizer/CPU reorganize the instructions in function f1()
such that B is sequenced-before A? This would cause C in function f2()
to evaluate to true
, but D would be false
; the assert could fire.
Is there anything preventing that issue from arising?
since there is no happens-before relationship between A and B
Wrong. [intro.multithread]/p14:
An evaluation A happens before an evaluation B if:
- A is sequenced before B, or
- A inter-thread happens before B.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With