Consider the following code:
int nonatom = 0;
std::atomic<int> atom{0};
// thread 1
nonatom = 1;
atom.store(1, std::memory_order_release);
// thread 2
while (atom.load(std::memory_order_relaxed)!=1); // spinlock waits for t1
atom.store(2, std::memory_order_relaxed);
// thread 3
if (atom.load(std::memory_oder_acquire)==2) // consider the case that this is true
int foo = nonatom; // read non-atomic
// Is foo guaranteed to be 1?
// Undefined behavior?
In the case that thread 3 reads the value 2
from atom
, is it guaranteed to see the value 1
in nonatom
?
Judging from the definition of the happens-before and synchronize-with relations, I would say that it cannot be said that the write to nonatom
happens-before the read, because the t3's acquire does not sync with the release in thread 1, because it does not read from the release-sequence but instead reads the value from a store of another thread, thread 2. In this case there would be a data-race between thread 1 and 3 because the operations compete for the same non-atomic and one does not happen-before the other.
However, it is commonly informally said that a release guarantees that writes cannot be reordered after it while an acquire guarantees that reads cannot be reordered before it, which would make it seemingly logically impossible for nonatom
to be read while or before it is written to.
My analysis of this is that by the standard alone, the code is incorrect, but could it actually break on any realistic implementation, given how release and acquire are usually implemented in machine code? What is your assessment of this example?