Sequential Consistency: The Model You Already Have
Everyone reasons about shared memory as if there were one global order of operations that every core agrees on. That model is intuitive, teachable, and not what any mainstream CPU implements — which is exactly why it is worth stating precisely before taking it away.
Software view, hardware view
The gap between what you wrote and what the machine does is where this whole domain lives.
The model, stated precisely
Sequential consistency (SC) is a specific, formal claim, not a vague feeling that "memory works normally". Lamport's definition has two parts: the result of any execution is the same as if all operations of all processors were executed in some single sequential order, and the operations of each individual processor appear in that order in the order specified by its program.
Picture a single switch in front of memory. Every core hands its loads and stores to the switch; the switch services them one at a time, in some interleaving; each core's own operations go through the switch in program order. Any interleaving is allowed, which is why concurrent programs are still hard under SC — but every core observes the *same* interleaving, which is what makes reasoning tractable.
It is worth being clear about what SC does *not* give you. It does not prevent races, it does not make counter++ atomic, and it does not stop two threads from interleaving in a catastrophic order. It gives exactly one thing: a single global order that everybody agrees on. That single guarantee is the foundation almost every informal correctness argument silently rests on, which is why losing it is so disorienting.
The litmus test that breaks it
The canonical demonstration is four lines long. Two variables start at zero. One core writes x then reads y; the other writes y then reads x. The question is whether both reads can return zero.
Under sequential consistency the answer is provably no. Whichever store the global order puts first, the core that performs its read later must see it. Enumerate all six interleavings and r1 == 0 && r2 == 0 appears in none of them. This is not a subtle argument — it is the kind of reasoning a competent engineer does at a whiteboard in ten seconds and is completely confident in.
On real x86-64 hardware, both reads return zero regularly. Not rarely, not under exotic conditions — run this in a loop on two cores and you will collect thousands of instances a second. The store sits in the writing core's store buffer, invisible to the other core, while the read proceeds against cache. Nothing is broken; the machine is behaving exactly as specified. It is the whiteboard argument that was wrong, because it assumed a model the hardware never promised.
1// Initially: x = 0, y = 02 3Core 0: Core 1:4 store x = 1 store y = 15 load r1 = y load r2 = x6 7// Under sequential consistency:8// whichever store is first in the global order,9// the later load must observe it10// => r1 == 0 && r2 == 0 is IMPOSSIBLE11//12// On real x86-64 hardware:13// r1 == 0 && r2 == 0 happens thousands of times per second14// because each store is still sitting in its own core's15// store buffer when the other core's load executesSequential consistency as something you buy, not something you get
The useful reframing is that SC is not the default that hardware sometimes violates — it is a guarantee with a price that you request where you need it. Every mainstream ISA gives you weaker ordering for free and lets you pay for stronger ordering with fences and with atomic operations that carry ordering semantics.
Languages sell you the same guarantee at a higher level, and on better terms. The C++ and Java memory models both offer a bargain usually called DRF-SC: if your program contains no data races — every conflicting access pair is ordered by synchronisation — then the implementation guarantees your program behaves sequentially consistently. You get the intuitive model back, provided you never race. The compiler inserts whatever fences the target ISA requires to make that true, which is why the same source is correct on both x86-64 and AArch64 even though the two hardware models differ substantially.
That bargain is the practical takeaway of this entire module. Use the language's synchronisation, and you may reason with the switch model. Reach past it — with plain loads and stores on shared data, or with relaxed atomics chosen for speed — and you have opted into the hardware model of whatever machine you happen to be on, which is a much less pleasant place to be. See Hardware Memory Models Are Not Language Memory Models for what those models actually permit and Memory Barriers: Ordering, Not Flushing for the instructions that buy ordering back.
| Sequential consistency | Real hardware (x86-64) | Data-race-free C++ / Java | |
|---|---|---|---|
| Single global order of all operations | Yes, by definition | No — each core has its own store buffer | Yes, provided the program has no data races |
| Store→Load reordering visible | Never | Yes, routinely | Not observable in a race-free program |
| Cost | Would require serialising memory traffic | Free — this is the default | The fences the compiler inserts at synchronisation points |
| What breaks the guarantee | Nothing; it is the model | Nothing; the model is weaker by design | A single data race anywhere — the guarantee is all-or-nothing |
| Where to read next | Why Your Loads and Stores Happen Out of Order | Store Buffers: Where Your Writes Wait | Hardware Memory Models Are Not Language Memory Models |
Key points
- Sequential consistency is a precise claim: one global order of all memory operations, with each core's operations appearing in program order.
- It is the model almost every informal correctness argument assumes, and no mainstream CPU implements it.
- The store-buffer litmus test forbids
r1 == 0 && r2 == 0under SC; x86-64 produces it thousands of times per second. - SC is a guarantee you purchase with fences and synchronisation, not a default the hardware occasionally violates.
- DRF-SC is the practical bargain: write a race-free program and the language gives the intuitive model back on every target.
Progressive depth
Overview
Sequential consistency means every core agrees on one global order of memory operations, and each core's own operations appear in program order within it. It is the model you already have in your head, and real hardware does not provide it.
Practical
Assume nothing about the order in which another thread observes your writes unless you have synchronised. Use the language's locks and atomics; they insert whatever the target ISA needs. The practical rule is DRF-SC — no data races means you may reason with the intuitive model.
Advanced
Learn to read litmus tests. State the outcome you believe impossible, then check it against the target model rather than against intuition. The store-buffer test distinguishes SC from TSO; the message-passing and independent-reads tests distinguish TSO from weaker models such as AArch64 and POWER.
Internals
Formal models add a further distinction most engineers never need: multi-copy atomicity, meaning whether a store becomes visible to all other cores at the same instant. x86-64 TSO and ARMv8 AArch64 are other-multi-copy-atomic; POWER historically was not, so two observers could disagree about the order of two stores from two different cores. This is where informal reasoning stops working entirely and tooling becomes mandatory.
Follow the mechanism
The path through the machine, hop by hop — and the conclusions it invites that are wrong.
- 1Core 0 → store buffer:
store x = 1retires immediately into the local store buffer; the instruction is complete from Core 0's point of view. - 2Store buffer → cache: the store drains toward L1 and the coherence protocol at some later, unspecified point.
- 3Core 1 → its own cache:
load r2 = xexecutes against Core 1's cached copy, which still holds the old value because the store has not drained. - 4Core 1 → store buffer: symmetrically, Core 1's
store y = 1is sitting in *its* store buffer while Core 0 loadsy. - 5Both cores → observed result: each load returns zero, an outcome that no single global ordering of the four operations can produce.
- • "The test passed on my machine ten million times, so the code is correct" — x86-64 forbids three of the four reorderings, so it cannot exhibit most weak-memory bugs at all.
- • "Adding a
volatilefixed it" — in C and C++volatileconstrains compiler optimisation of that access and carries no inter-thread ordering guarantee; the fix was almost certainly coincidental timing. - • "Sequential consistency means my code is thread-safe" — SC still permits every interleaving; it only guarantees everyone sees the *same* interleaving.
- • "This is a compiler bug" — the compiler is usually exploiting exactly the latitude the language model grants it.
Consequences, controls and cost
- • Whiteboard correctness arguments about shared memory are unsound by default, because they assume a global order the machine does not provide.
- • Bugs of this class are invisible on one core, rare under load on x86-64, and much more frequent on weakly-ordered hardware such as AArch64 — so they surface as "only reproduces on the mobile build".
- • Adding a debugger, a print statement or a sleep frequently makes the bug disappear, because anything that drains the store buffer restores the intuitive behaviour.
- • Lock-free code written and tested only on x86-64 carries an unquantified risk of being wrong everywhere else.
- • Write data-race-free programs and use the language's synchronisation primitives — this buys back the sequentially consistent model on every target and is the only approach that scales.
- • When you do use atomics directly, prefer the default sequentially consistent ordering until profiling proves it matters; relaxed orderings are an optimisation with a correctness cost.
- • Reason with litmus tests rather than intuition when writing lock-free code: state the outcome you believe is impossible and check it against the target's memory model.
- • Run concurrency tests on weakly-ordered hardware, not only on x86-64 — a clean x86 run is weak evidence.
- • Use a thread sanitiser: data races are exactly the precondition under which the DRF-SC guarantee evaporates, and tooling finds them far more reliably than review.
- • Write the store-buffer litmus test as a tight two-thread loop and count occurrences of `r1 == 0 && r2 == 0`; on x86-64 it appears within seconds.
- • Run the same test on an AArch64 machine and compare rates, then add a full fence between the store and the load and confirm the outcome disappears.
- • Use a thread sanitiser (`-fsanitize=thread`) to detect the data races that void the DRF-SC guarantee.
- • For real code, use a model checker or litmus-test tool that enumerates outcomes against a formal memory model rather than relying on execution.
- • Requesting sequential consistency everywhere costs fences on weakly-ordered hardware, and those fences can dominate a tight synchronisation loop.
- • The DRF-SC bargain is all-or-nothing: one data race anywhere in the program voids the guarantee for the whole program, not just for the racing variable.
- • Litmus-test reasoning is precise but slow, and does not scale to whole programs — which is the argument for using locks and letting the library authors do this work.
Scope
§224 — what these claims are specific to.
- GENERALSequential consistency itself is a formal model, not a property of any specific machine; the definition is universal.
- ISA-SPECIFICThe claim that the store-buffer litmus test produces
r1 == 0 && r2 == 0is specific to x86-64 TSO and weaker models; a hypothetical SC machine would forbid it. - PLATFORM-SPECIFICThe DRF-SC guarantee is a property of the C++11-and-later, Java 5-and-later, Go and Rust memory models. Older or informal language specifications do not provide it.
Misconceptions
Where the rest of this lives
This lesson gives you the hardware's ordering guarantees. Turning those into an argument that a specific algorithm is correct — happens-before edges, linearizability, what a lock actually establishes — is a different skill and belongs there.
The DRF-SC bargain is defined by the language specification, not the CPU. Exactly which operations create synchronisation edges, and what a relaxed atomic permits, is a language-level question.