f() is sequenced before g(). If g() preforms an release on an atomic 'gatom', then it synchronizes with a future thread that does an aquire on 'gatom'. Then in that thread, anything sequenced after that aquire can assume that result_of_f has been properly set. It cannot read from result_of_f prior to the aquire on 'gatom', because that would be a data race, and therefore undefined.Originally Posted by C committee draft from April 12, 2012
Simmilarly in the other direction. The aquire of 'gatom' implies that everything sequenced before the previous release of gatom in another thread. That includes any writes to result_of_h. As a result, those writes to result_of_h does not constitute a data race with that prior write of result_of_h.
This two way release and acquire also ensures a thread that acquires 'gatom' after this thread knows that it cannot write to result_of_h, because it is not inter thread happends before the write in this thread to result_of_h, and no other inter thread sequencing exists.
It's not a matter of recognising sequence points or not. C simply does not define the order in which operations are done, except where there is a dependency or IO.So, we wind up back at the compiler and sequence points. If the compiler doesn't recognize a sequence point, and so reorders, the use of atomic doesn't buy you correct ordering. Any given compiler may recognize any specific atomic operation as a sequence point, but that is not something enforced by the standards. (Well, I do not know C11 at all so it may be something in that standard.) If a compiler does recognize atomic reads and writes and necessary sequence points the compiler doesn't reorder so you get the canonical ordering.