Suppose a debug dump shows 1 near C0 and 0 near C1. Is the machine broken? Only if those values are usable in a way that violates the coherence contract. Prerequisite: the previous lesson's E, M and I meanings. Here S adds shared, read-only permission.
First remove coherence altogether. C0 and C1 still have private write-back caches and H is only a memory controller: it keeps no directory and sends no Inv or Fwd. Memory holds X = 0 and both caches are empty. Three steps are enough to break the program.
Step | C0 copy / value | C1 copy / value | Memory | Newest data |
|---|---|---|---|---|
Initial | empty / - | empty / - | 0 | Memory: 0 |
1. Both load X | valid / 0 | valid / 0 | 0 | C0, C1 and memory: 0 |
L2. C0 stores 1 | valid / 1 | valid / 0 | 0 | C0: 1 |
L3. C1 loads X | valid / 1 | valid / 0, returns 0 | 0 | C0: 1; C1's load returned stale 0 |
The read and fill arrows are plain memory accesses, not coherence messages, and there is no state letter to show: each copy is simply valid. Step L3 is the failure. C1's load comes after C0's store in the history of X, yet it returns the old value, and nothing in the system will correct C1's copy while it stays cached. Candidates A-D below ask which snapshots a protocol must forbid.
Now add a protocol and inspect proposed settled snapshots, not a message trace; each row is a separate candidate, not the next step of one execution. Select MSI, where shared copies are clean. Assume no pending messages, no uncompleted stores and no additional agents. A write epoch is an interval in which one cache holds exclusive write permission for the line; a read epoch is an interval with no writer, in which zero or more caches hold read-only copies.
Candidate | C0 state / value | C1 state / value | H directory | Memory | Diagnosis |
|---|---|---|---|---|---|
A: private dirty value | M / 1 | I / - | exclusive(C0) | 0 | Legal if 1 is the last store; C0 preserves it |
B: conflicting permission | M / 1 | S / 1 | exclusive(C0) | 0 | Illegal: C1 can read while C0 may write |
C: disagreeing readers | S / 1 | S / 0 | S{C0,C1} | 1 | Illegal: two readers in one read epoch disagree; with memory = 1 under MSI, C1's 0 is stale |
D: idle read epoch | S / 0 | S / 0 | S{C0,C1} | 0 | Legal before any store; no active writer |
Candidate B is already unsafe even though both caches contain 1. M authorises C0 to store again without consulting C1. The independent S copy would remain readable, so granting those permissions together is the error. Waiting until the values visibly diverge detects the consequence too late. Candidate C shows the opposite lesson: read-only permissions can be compatible while the values are wrong.
Candidate A permits old memory because the directory forwards conflicting requests to C0, the dirty-responsible holder. C1's I entry has no architectural value to compare. If a raw array dump still exposes old bits there, annotate them as invalid rather than reporting a stale load. The permission-and-value split follows the Primer, second edition, Chapter 2: both invariants are necessary.
A useful bug report records the line address, permissions, directory entry, returned load value and position in the coherence history. Two loads separated by a store may correctly differ. These candidates instead compare simultaneous settled permissions or readers within one read epoch. A read epoch may have zero readers, and nothing requires a core to hold write permission while the line is idle.
No. C0 still has independent write permission while C1 has read permission. Updating memory repairs neither the overlapping rights nor C1's exposure to C0's next silent store.