1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! loom model-check of the **publication protocol** for the epoch tier
//! (Phase 3b-II).
//!
//! # Scope — what loom covers and what it does NOT
//!
//! loom and `crossbeam-epoch` do NOT compose: loom replaces the global
//! allocator and the atomics with its own mock, so `crossbeam_epoch::Atomic`
//! and `epoch::pin` cannot run inside a `loom::model`. This harness therefore
//! models the **publication protocol** in isolation — the seqlock-style
//! ordering between a `generation` counter and a `value` — using
//! `loom::sync::atomic` (NOT crossbeam). It asserts the core safety property:
//!
//! > A reader using the seqlock protocol (load gen → load value → re-load gen;
//! > accept only if both gens match AND equal the expected generation) NEVER
//! > resolves a value belonging to a different generation.
//!
//! This mirrors `AtomicSlot::read_with` exactly. The writer mirrors
//! `AtomicSlot::install` (write value, generation unchanged) and
//! `AtomicSlot::evict` (swap value to a tombstone, then bump generation).
//!
//! # What rests on miri, NOT loom
//!
//! The **reclamation correctness** (the `guard.defer_destroy` / epoch-advance
//! lifetime proof in `src/concurrent/hand.rs`) is NOT modelled here. That
//! rests on the `crossbeam-epoch` crate's correctness plus `miri`, which
//! verifies our `unsafe` dereferences against real epoch guards. loom models
//! ordering; miri models lifetime/aliasing. (See the final report: miri cannot
//! run the epoch tests because crossbeam-epoch 0.9.18's global collector is
//! itself not miri-clean — an upstream limitation, not our code.)
//!
//! # How to run
//!
//! loom is a `cfg(loom)` dev-dependency, so this file is only compiled under
//! `--cfg loom`:
//!
//! ```sh
//! RUSTFLAGS="--cfg loom" cargo test --features experimental --test loom_epoch
//! ```
use ;
use Arc;
use thread;
/// Sentinel for "vacant" (the null pointer in the real `AtomicSlot`). A reader
/// that loads this resolves to `None` (I2 — tombstone).
const VACANT: usize = 0;
/// The shared publication state, mirroring a single `AtomicSlot<T>`:
/// a generation counter and a value word. We use `AtomicUsize` for the value
/// (not a raw pointer) because loom models *ordering*, not freeing — the value
/// is a stand-in for the "pointee contents" a real reader would observe.
/// Reclamation is LEAKED under loom (a value is never freed), which is fine:
/// we are checking the generation/value ordering protocol, not the reclamation
/// lifetime.
/// loom model-check: 1 writer + 1 reader over a single `(generation, value)`
/// publication. The writer churns install → evict cycles (publishing a tagged
/// value, then tombstoning it and bumping the generation), modelling the real
/// `insert`/`remove` churn across a generation boundary. The reader probes with
/// the seqlock protocol. The assertion: a reader NEVER resolves a value to a
/// generation it does not belong to — every resolved value equals the tag the
/// writer published at the reader's observed generation.
///
/// **Bounded exploration** (`preemption_bound = 3`) keeps the check to a few
/// seconds while still covering every interleaving with up to 3 preemptions —
/// enough to expose the torn read this protocol must prevent (verified:
/// removing the seqlock re-check in `read_with` makes loom fail here).
/// Unbounded exploration of this model is combinatorially explosive and
/// impractical for the dev loop. A single reader suffices: the hazard is
/// reader-vs-writer, not reader-vs-reader.