Every C++ object has a modification order: the order of all writes to that object, beginning with its initialization. The order may differ between runs, but all threads in one execution must agree on it. This is a per-object rule, not a single timeline for the whole program: threads need not agree on the relative order of operations on different objects. section 5.1.3 section 5.3.3
For a non-atomic object shared between threads, you must provide sufficient synchronization; conflicting unsynchronized accesses can produce a data race and undefined behavior. Atomic operations keep accesses to their object within defined behavior, but they do not necessarily establish an order between different objects. section 5.1.3 section 5.1.2
Seeing a flag does not necessarily reveal an earlier write
Suppose one thread stores true to atomic x and then to atomic y, both with memory_order_relaxed. Another thread waits until it loads true from y, then loads x. That final load can still read false. Each object has a modification order, but relaxed operations do not make the writer's order across x and y visible as one order to the reader. If the store to y uses memory_order_release and the successful load of y uses memory_order_acquire, the earlier store to x becomes visible to the subsequent load, even when those x operations remain relaxed. section 5.3.3
Listing 5.5. Relaxed operations have few ordering requirements
#include <atomic>
#include <thread>
#include <assert.h>
std::atomic<bool> x,y;
std::atomic<int> z;
void write_x_then_y()
{
x.store(true,std::memory_order_relaxed); #1
y.store(true,std::memory_order_relaxed); #2
}
void read_y_then_x()
{
while(!y.load(std::memory_order_relaxed)); #3
if(x.load(std::memory_order_relaxed)) #4
++z;
}
int main()
{
x=false;
y=false;
z=0;
std::thread a(write_x_then_y);
std::thread b(read_y_then_x);
a.join();
b.join();
assert(z.load()!=0); #5
}
!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":7,\"ch\":49},{\"line\":7,\"ch\":51}],[{\"line\":8,\"ch\":49},{\"line\":8,\"ch\":51}],[{\"line\":12,\"ch\":49},{\"line\":12,\"ch\":51}],[{\"line\":13,\"ch\":49},{\"line\":13,\"ch\":51}],[{\"line\":25,\"ch\":49},{\"line\":25,\"ch\":51}]]"}
!@%STYLE%@!
The two-thread relaxed-atomic listing makes the surprising result concrete: its final assertion can fail even though the writer stores to x before y. section 5.3.3
Modification order still limits what one thread can observe of a single object. After a thread has seen a particular value, a later read cannot go backward to an earlier value in that object's order. A read following a write to the same object in the same thread must see that write or a later one. These limits do not, by themselves, create a total order across objects; sequential consistency supplies stronger ordering, while acquire-release can introduce selected ordering relationships. section 5.1.3 section 5.3.3


