With release / acquire: why the same code reads 42 1 2 3 4 4 5 6 7 // at the start: data = 0, ready = false // core 0 (writer) data = 42; ready.store(true, release); // core 1 (reader) while (!ready.load(acquire)) {} use(data); // reads 42 Core 0 · writer data, then ready (release) invalidate queue store buffer not used here data = 42 ready = 1 data waits for ACKs release: queued behind data drained to L1D in order L1D (private) data 0 S data 42 M data 42 S ready 0 E ready 1 M ready 1 S L2 (private) Core 1 · reader ready (acquire), then data invalidate queue store buffer invalidate data ACK sent, not processed acquire: queue processed not used here L1D (private) data 0 S data 0 S invalidation queued data — I invalidated data 42 S fetched 42 again ready none I ready 1 S L2 (private) 1 2 3 4 ready's line moves to core 1 5 data's line moves to core 1 7 acquire: drain the queue 6 On-chip interconnect (ring or mesh): Read, RFO, invalidate, ACK and cache-to-cache lines all go here L3 (shared by all cores) + snoop filter The snoop filter records which cores hold each line, so invalidations go only to cores with a copy. New values are never pushed: core 1 fetches both ready and data itself (steps 5 and 7).

MmodifiedEexclusiveSsharedIinvalid