Where does a coherence bug begin: at the stale load, or earlier? Often the first failure is a grant that permits an unsafe access. The bad load only makes that failure visible. A useful debug explanation identifies both events and connects them with a state and value history, instead of starting and ending with 'the caches disagree'.
Prerequisite: Invalidate Versus Update. This deliberately broken MSI example begins at the same settled shared snapshot: S / 0, S / 0, H = S{C0,C1}, memory = 0; all preceding transactions are complete.
Inject one defect: H skips invalidating C1 and sends writable Data to C0. This is not a legal alternative transaction. The trace uses no concurrent requests, so a fabric race cannot explain the failure. Data at step 2 is permission-only; C0 already has 0.
Step | C0 state / value | C1 state / value | H directory | Memory | Newest data |
|---|---|---|---|---|---|
Initial | S / 0 | S / 0 | S{C0,C1} | 0 | C0, C1, memory: 0 |
1. GetM received | SM^D / 0 | S / 0 | S{C0,C1}, pend GetM C0, acks 0/1 | 0 | C0, C1, memory: 0 |
2. BUG Data received | M / 0 | S / 0 | S{C0,C1}, pend GetM C0, BUG: no Inv sent | 0 | All still 0, SWMR already fails |
(assumed) grant delivered | M / 0 | S / 0 | exclusive(C0), wrong: C1 not revoked | 0 | All still 0, SWMR still fails |
L. C0 local store | M / 1 | S / 0 | exclusive(C0), wrong | 0 | C0: 1, C1 and memory stale |
L. C1 local load returns 0 | M / 1 | S / 0 | exclusive(C0), wrong | 0 | C0: 1, returned 0 is wrong |
Step 2 is the first safety violation even though all bits still agree. C1 remains allowed to serve hits while C0 has permission to change X. At C0's store the values diverge. Stipulate that C0's store has completed at the coherence interface before C1's load is issued, with neither operation speculative or buffered outside this trace. With this ordering and no intervening store, returning 0 is the wrong-value symptom. No newest value is hidden in a Data payload here because the only Data is permission-only.
Observation | Conclusion on the stated model | Evidence to preserve |
|---|---|---|
M / 0 beside S / 0 at step 2 | Permission failure before values differ | Writable grant and C1's still-usable S permission |
M / 1 beside I / - with memory = 0 | Can be legal write-back | C0's permission and responsibility for value 1 |
S / 1 beside S / 1 after final write value 2 | Value failure despite legal permissions | Reference write history, not just current agreement |
An older result in an unqualified software log | Insufficient evidence by itself | Named memory model, synchronization and operation order |
The last three rows are independent diagnostic classifications, not extra transactions. Check permission history, then value history. The two-invariant approach follows the Primer, second edition, Chapter 2. Also record H's pending transaction and acknowledgements: a directory saying exclusive cannot override an actual reader that was never revoked.
The repair is the acknowledgement boundary from the preceding lesson: revoke C1, collect its Inv-Ack at H, then send permission-only Data. Waiting an arbitrary number of cycles is not equivalent evidence when delivery is reliable but variably delayed. This example establishes a safety bug; it makes no bounded-latency or eventual-completion claim. Language data races and cross-location ordering need their own model before interpreting software outcomes.
Yes. C0 = M / 0 and C1 = S / 0 grants mutually incompatible access even without a store. Permission safety is an invariant, so a trace need not reach a wrong-value load before it is rejected.