Orderingsequential consistencymemory modellitmus testshared memory

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.

Follow the mechanism

Software view, hardware view

The gap between what you wrote and what the machine does is where this whole domain lives.

The question
What is the mental model of shared memory that almost everyone starts with, and in what precise way is it wrong?
What you wrote
Memory is a box of variables. Each core reads and writes it one operation at a time, and every core sees the same sequence of events. If I write `x = 1` before I read `y`, then any core that sees my read must also be able to see my write.
What the hardware does
Each core has its own store buffer and its own cache, and a store becomes visible to other cores at some point *after* the instruction retires on the writing core. There is no single global sequence; there is a partial order that the coherence protocol and the memory model together constrain.
Sequential consistency is the model every correctness argument is implicitly written against, so the exact way real hardware departs from it is the exact shape of the bugs you will ship. Those bugs do not fail on a single core, do not fail under a debugger, and frequently do not fail on the developer's x86 laptop.
SourceCompilerInstructionsFront EndExecutionRegistersCachesMemoryI/OBehavior

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.

Sequential consistency as a switch in front of memory
every core sees this same orderCore 0 · program order preservedCore 1 · program order preservedCore 2 · program order preservedSwitch: one operation at a timeMemory · one global sequence
UserLLMAgentToolDataDecisionHumanGuardrail

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.

The store-buffer litmus test. Forbidden under SC, permitted and frequently observed on x86-64.
1// Initially: x = 0, y = 0
2
3Core 0: Core 1:
4 store x = 1 store y = 1
5 load r1 = y load r2 = x
6
7// Under sequential consistency:
8// whichever store is first in the global order,
9// the later load must observe it
10// => r1 == 0 && r2 == 0 is IMPOSSIBLE
11//
12// On real x86-64 hardware:
13// r1 == 0 && r2 == 0 happens thousands of times per second
14// because each store is still sitting in its own core's
15// store buffer when the other core's load executes

Sequential 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.

Three models, three different sets of promises
Sequential consistencyReal hardware (x86-64)Data-race-free C++ / Java
Single global order of all operationsYes, by definitionNo — each core has its own store bufferYes, provided the program has no data races
Store→Load reordering visibleNeverYes, routinelyNot observable in a race-free program
CostWould require serialising memory trafficFree — this is the defaultThe fences the compiler inserts at synchronisation points
What breaks the guaranteeNothing; it is the modelNothing; the model is weaker by designA single data race anywhere — the guarantee is all-or-nothing
Where to read nextWhy Your Loads and Stores Happen Out of OrderStore Buffers: Where Your Writes WaitHardware 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 == 0 under 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.

  1. 1
    Core 0 → store buffer: store x = 1 retires immediately into the local store buffer; the instruction is complete from Core 0's point of view.
  2. 2
    Store buffer → cache: the store drains toward L1 and the coherence protocol at some later, unspecified point.
  3. 3
    Core 1 → its own cache: load r2 = x executes against Core 1's cached copy, which still holds the old value because the store has not drained.
  4. 4
    Core 1 → store buffer: symmetrically, Core 1's store y = 1 is sitting in *its* store buffer while Core 0 loads y.
  5. 5
    Both cores → observed result: each load returns zero, an outcome that no single global ordering of the four operations can produce.
What people conclude from this — wrongly
  • "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 volatile fixed it" — in C and C++ volatile constrains 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

What it causes
  • • 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.
What you can do
  • • 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.
How to see it
  • • 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.
What it costs
  • • 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.

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 == 0 is 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

Claim
“Modern CPUs execute memory operations in the order I wrote them.”
Reality
No mainstream CPU does. x86-64 permits store→load reordering; AArch64 and POWER permit substantially more. A single core hides this from itself perfectly, which is why the belief survives.
Claim
“Sequential consistency would make concurrent programming easy.”
Reality
It would remove one specific difficulty — disagreement between cores about the order of events. Every interleaving hazard, race and lost update remains. SC machines still need locks.
Claim
“If I never use atomics, memory ordering does not apply to me.”
Reality
It applies precisely when you *do not* use them. Plain loads and stores on shared data are exactly the case the hardware feels free to reorder; atomics are the tool that constrains it.

Where the rest of this lives

Concurrency & Parallelism
Reasoning about interleavings and happens-before

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.

Programming Languages & Runtime Internals
Language memory models

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.