from http://en.cppreference.com/w/cpp/atomic/memory_order
So marking up the first example:Explanation
Relaxed ordering
Atomic operations tagged std::memory_order_relaxed are not synchronization operations, they do not order memory. They only guarantee atomicity and modification order consistency.
For example, with x and y initially zero,
// Thread 1:
r1 = y.load(memory_order_relaxed); // A
x.store(r1, memory_order_relaxed); // B
// Thread 2:
r2 = x.load(memory_order_relaxed); // C
y.store(42, memory_order_relaxed); // D
is allowed to produce r1 == r2 == 42 because, although A is sequenced-before B and C is sequenced before D, nothing prevents D from appearing before A in the modification order of y, and B from appearing before C in the modification order of x.
Typical use for relaxed memory ordering is updating counters, such as the reference counters of std::shared_ptr, since this only requires atomicity, but not ordering or synchronization.
Thread 1:
r1 = x.load( memory_order_relaxed ); // A
if ( r1 == 42 ) y.store( r1, memory_order_relaxed ); // B
Thread 2:
r2 = y.load( memory_order_relaxed ); // C
if ( r2 == 42 ) x.store( 42, memory_order_relaxed ); // D
the same explanation still holds as the single-thread sequencing was apparently already established without the conditional, but absent a total order the loads to r1 and r2 are apparently allowed to see the future of the other thread, just not their own and thus contain 42... which while i think is ridiculous, apparently the only fallacy here with memory_order_relaxed and a malicious compiler is that two different memory locations are being used and reliant on each other and synchronisation would be needed to cover interactions between them in this manner.