Given:
std::atomic<uint64_t> x;
uint64_t f()
{
x.store(20, std::memory_order::memory_order_relaxed);
x.store(10, std::memory_order::memory_order_relaxed);
return x.load(std::memory_order::memory_order_relaxed);
}
Is it ever possible for f to return a value other than 10, assuming there is only one thread writing to x? This would obviously not be true for a non-atomic variable, but I don't know if relaxed is so relaxed that it will ignore data dependencies in the same thread?
The result of the load is always 10 (assuming there is only one thread). Even a relaxed atomic variable is "stronger" than a non-atomic variable:
A relaxed atomic variable can't be used to synchronize different threads with each other, unless accompanied by explicit fences. That's the sense in which it's relaxed, compared with the other memory orderings that are applicable to atomic variables.
For language lawyering, see C++20 [intro.races]/10:
and [intro.races]/15:
and [intro.races]/18:
Thus, in your program, the store of 20 happens before the store of 10 (since it is sequenced before it) and the store of 10 happens before the load. The write-write coherence requirement guarantees that the store of 10 occurs later in the modification order of
xthan the store of 20. When the load occurs, it is required to take its value from the store of 10, since the store of 10 happens before it and there is no other modification that can follow the store of 10 in the modification order ofx.