If every cache has a legal permission, can a load still get the wrong answer? Yes. A controller can revoke the old writer correctly and then refill (fetch the line's bytes again) from stale memory. SWMR checks who may access X; the data-value invariant checks what value must survive that transfer. A permission-only checker would miss this failure.
Prerequisite: SWMR Precisely. The Primer, second edition, Chapter 2 relates each epoch's starting value to the preceding write epoch's ending value. For our examples, initialisation supplies 0 before any write epoch. Within a write epoch, the sole writer's own stores advance the value; a subsequent reader must inherit the final value.
Return to the course model: reset has I / - in both caches, H = U, memory = 0. For the following independent candidate snapshots, assume the last completed write epoch ended with X = 2 and there has been no later store. The reference value is therefore 2. These rows classify possible settled snapshots; they are not successive messages or a dirty-read transaction.
Candidate | C0 state / value | C1 state / value | H directory | Memory | Newest data / verdict |
|---|---|---|---|---|---|
MSI writable holder | M / 2 | I / - | exclusive(C0) | 0 | C0: 2, legal, memory is stale |
MSI or MESI clean sharing | S / 2 | S / 2 | S{C0,C1} | 2 | C0, C1, memory: 2, legal |
MOESI dirty sharing | O / 2 | S / 2 | O: C0, sharers {C1} | 0 | C0 and C1: 2, C0 dirty-responsible, legal |
Lost final store | S / 1 | S / 1 | S{C0,C1} | 1 | Required 2 is lost, data-value violation |
Disagreeing readers | S / 2 | S / 0 | S{C0,C1} | 2 | C0 and memory: 2, C1 is stale, violation |
Every row has a legal count of writable permissions. The fourth row is nevertheless wrong: agreement among both caches and memory cannot erase the obligation to preserve 2. This is why a reference model needs store history, not merely a majority comparison. In the fifth row one correct reader cannot excuse the other reader's stale value.
The first row does not require immediate writeback. H must preserve access to C0's newer value and must not answer a competing request with memory's 0. The MOESI row separates read permission from dirty responsibility: both caches can read 2, but C0 remains responsible for the dirty data and neither may write. In our MSI and MESI model, settling a remote dirty read as S/S requires bringing memory up to date; MOESI may instead settle O/S with memory stale. These are different protocol choices, not interchangeable state names.
H's exclusive(C0) entry is permission metadata, not proof that memory is fresh: a MESI holder can silently change E to M after a local store, leaving that entry unchanged. Data in flight and H's buffers may also carry the newest value during a transfer. A checker must account for those locations before declaring the value lost; these settled classifications deliberately have no message in flight.
The expected value below comes from the completed write epoch, not from voting on the candidate copies. The second half compares a checker that tests every load against memory (dram_checker) with one that tests against the reference value: each makes the opposite call in the two cases. These assertions classify the stated settled examples only; they do not enumerate races or prescribe how a full scoreboard orders concurrent operations.
initial_value = 0
completed_write_epochs = [[1, 2]]
expected = initial_value
for stores in completed_write_epochs:
for value in stores:
expected = value
def read_epoch_values_match(readers, reference):
return all(value == reference for value in readers.values())
correct = {"C0": 2, "C1": 2}
agreed_but_stale = {"C0": 1, "C1": 1}
disagreeing = {"C0": 2, "C1": 0}
assert expected == 2
assert read_epoch_values_match(correct, expected)
assert not read_epoch_values_match(agreed_but_stale, expected)
assert not read_epoch_values_match(disagreeing, expected)
def dram_checker(load, mem):
return load == mem
def reference_checker(load, reference):
return load == reference
# Case 1: C0 is M / 2, memory = 0. A load of 2 from C0 is legal.
assert not dram_checker(2, 0) # false failure
assert reference_checker(2, expected) # correctly accepted
# Case 2: memory = 1 but the reference value is 2. A load of 1 is stale.
assert dram_checker(1, 1) # false pass
assert not reference_checker(1, expected) # correctly rejected
print("PASS: agreement and memory comparison do not establish the required value")No. With no later store, the next epoch must inherit 2. Agreement does not repair a lost update; the store history supplies the expected value. A correct checker rejects the snapshot even though SWMR passes.