Before you can read the parent note Memory Model, you need every word and symbol it silently assumes. We build them one at a time, from nothing, each on top of the last.
Look at the figure: two shelves (two CPU cores), and boxes on each. The key trouble — which this whole topic is about — is that when core 1 changes a box, core 2 does not see the change instantly. There's a delay, drawn as the wavy pipe between them.
The picture: two people (Thread 1, Thread 2) each holding a to-do list, both reaching for the same boxes. Everything hard in this topic happens because they share memory but run at their own pace.
Why the topic needs it: a data race, happens-before, acquire, release — all of these describe relationships between two threads. With one thread there is no problem at all.
The picture: an arrow pointing straight down the to-do list. This is the order you wrote, not necessarily the order the machine does things — which is the whole surprise of the next symbol.
In the figure the black arrow is the order you wrote; the red arrow is an order the machine might actually run. Three separate villains can cause this swap:
the compiler (see Compiler reordering and the as-if rule),
The picture: a box with a solid lid, so anyone looking in sees either the old value or the new value, never a smeared mix. Important and easy to miss: atomic guarantees this only for the atomic box itself — it says nothing yet about the ordinary boxes around it. Ordering those is a separate job (that's what §9–11 are for).
The picture: store = dropping a note in; load = peeking in; RMW = grabbing the note, editing it, and putting it back before anyone else can touch the box.
Why the topic needs it: the entire memory model exists to let you avoid data races by supplying the missing ordering arrow. Everything from here is about building that arrow correctly.
We must meet these two words before we can define the cross-thread arrow, because that arrow is made of them.
The figure shows release as a floor (⬇ things can't fall through) and acquire as a ceiling (⬆ things can't rise through). Together, when the acquire-load reads exactly the value the release-store wrote, a handshake forms — the cross-thread arrow of the next section.
A mutex is just these two glued together: locking is an acquire, unlocking is a release.
An "event" here just means one read or write happening. We connect events with arrows. First, two tiny pieces of notation so the arrows read cleanly:
AhbB⟹B sees every write done by A
That formula is the payoff. If — and only if — you can draw a happens-before path from a write to a read, the read is guaranteed to see that write. No path ⇒ no guarantee ⇒ possible data race.
Follow the figure: the solid down-arrows are sequenced-before, the dashed cross-arrow is synchronizes-with, and the long green path from (1) to (4) is the happens-before chain that makes data == 42 safe.
Picture: a dial from "loose" (relaxed) to "strict" (seq_cst). More strictness = more guarantees = usually slower, because the CPU must insert real fences (see std-atomic_thread_fence).
Each foundation feeds the next; the two arrows that finally meet at Happens-before are the whole point — one from inside a thread (sequenced-before), one across threads (synchronizes-with).