Considering the following example:
private int sharedState = 0;
private void FirstThread() {
Volatile.Write(ref sharedState, 1);
}
private void SecondThread() {
int sharedStateSnapshot = Volatile.Read(ref sharedState);
Console.WriteLine(sharedStateSnapshot);
}
Until recently, I was under the impression that, as long as FirstThread()
really did execute before SecondThread()
, this program could not output anything but 1.
However, my understanding now is that:
- Volatile.Write() emits a release fence. This means no preceding load or store (in program order) may happen after the assignment of 1 to
sharedState
. - Volatile.Read() emits an acquire fence. This means no subsequent load or store (in program order) may happen before the copying of
sharedState
tosharedStateSnapshot
.
Or, to put it another way:
- When
sharedState
is actually released to all processor cores, everything preceding that write will also be released, and, - When the value in the address
sharedStateSnapshot
is acquired;sharedState
must have been already acquired.
If my understanding is therefore correct, then there is nothing to prevent the acquisition of sharedState
being 'stale', if the write in FirstThread()
has not already been released.
If this is true, how can we actually ensure (assuming the weakest processor memory model, such as ARM or Alpha), that the program will always print 1? (Or have I made an error in my mental model somewhere?)