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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//! Loom models for `crate::sync`.
//!
//! ```text
//! RUSTFLAGS="--cfg loom" cargo test --release --lib -- sync::loom_tests::primitives
//! RUSTFLAGS="--cfg loom" cargo test --release --lib -- sync::loom_tests::primitives -- --ignored
//! ```
//!
//! The second line runs the `#[ignore]`d twins: the models whose three
//! threads all contend for one lock, at a deeper preemption bound (see
//! `contended!`).
//!
//! Under `--cfg loom` every atomic and protocol cell in the primitives is
//! loom's instrumented one, so these models check the wait protocol itself
//! (the arrival stack, the drain role, the waiter state machine, the
//! waker slot and the per-primitive state words) under every interleaving
//! loom reaches within the preemption bound. What each model proves:
//!
//! - **Mutual exclusion**: the guarded value is a `loom::cell::UnsafeCell`,
//! so two holders at once fail the model as a data race.
//! - **No lost wakeup**: every wait goes through `loom::future::block_on`,
//! which parks the loom thread until its waker fires; a wakeup that is
//! never delivered leaves every thread parked and loom reports the
//! deadlock.
//! - **Barging with bounded bypass**: `MAX_BYPASS` is 2 under loom, so a
//! waiter that a barging thread keeps passing over reaches its handoff
//! within a few operations, and the model covers the owed state, the
//! handoff and the return to barging.
//! - **Cancellation at every await point**: a future polled once and then
//! dropped races the release that may hand it the lock or nudge it;
//! whatever it was given must reach the next waiter.
//! - **No access after free**: a waiter node handed back to the allocator
//! counts as written (`Waiter::retire`), so a party that touches a node
//! after letting go of its reference fails every model as a race.
//! - **Reentrancy depth**: an owner nests and unwinds while another owner
//! waits.
//! - **The upgradable read**: it shares with readers, upgrades while they
//! come and go, and excludes writers. A dropped upgrade never lets a
//! writer in beside the next holder of the upgradable read; a calibration
//! rebuilds the pass that took the write for a dropped upgrade's node and
//! must fail.
//!
//! - **A drain that ends**: a waiter that serves the queue itself while the
//! holder lets go and barges back runs a bounded number of passes and is
//! served; and a thread that holds the drain role over an empty queue
//! runs one pass, whatever another thread releases. Those models count
//! passes, which the public API cannot see, so they live in
//! `src/sync/loom_model.rs`, each with a calibration that writes its step
//! the wrong way and must fail.
//!
//! - **The spins**: under loom a spin has one round. Every model here
//! runs the spin before queueing; the spin on the waiter's node is
//! reached only where a model makes sure of it, because a spin that runs
//! out halves the budget to zero rounds. Two models check the spins
//! directly on the production semaphore: the Mutex's inline first attempt
//! refuses a lock owed to a queued waiter, and a nudge that lands while
//! the waiter spins on its node, calling no waker, or just after the
//! spin's last read, is never lost; the second counts the spins on the
//! node and the marks each way and fails if any count is below its floor.
//! Each has a calibration that takes the guard away and must fail.
//!
//! - **A semaphore that closes**: `close` races a queued acquire, a release
//! with an acquire, and a cancelled acquire. Every wait ends (a wait left
//! parked is a deadlock), none gets a permit the close had refused, and the
//! permits free at the end are the permits there were: none leaks, none is
//! counted twice.
//! - **A write that becomes a read**: the downgrade lets no writer in
//! between (the guarded value is a `loom::cell::UnsafeCell`, so a writer
//! inside beside the read is a race), and wakes the readers queued behind
//! the write to share it; a bounded lock never has more readers inside than
//! it was built for. The calibration of the downgrade releases the write
//! and reads again, and must lose the value to a writer.
//! - **Notify's contract**: a `Notified` made before `notify_waiters` ends
//! wherever its first poll lands, `notify_one` with nobody waiting stores
//! exactly one permit, `notify_last` is never lost, and an enabled future
//! receives a `notify_one` sent before its first poll.
//! - **The cells**: `set` racing an initializer leaves one value, and a
//! `SetOnce` waited on is woken by the one `set` that wins.
//!
//! The waiter-node free list is a pass-through to the allocator under loom
//! (see `Pool`), so node recycling itself is covered by miri and the unit
//! tests rather than here. The calibration models deliberately expect
//! something the protocol does not guarantee and must fail; without them
//! a search that never reached the bad interleaving would pass for the
//! same reason a broken one does.
use Future;
use Pin;
use ;
use ;
use UnsafeCell;
use ;
use ;
use thread;
use crate;
/// Runs `model` under loom with at most `preemptions` preemptions per
/// schedule (`LOOM_MAX_PREEMPTIONS` overrides it) and fails it if the
/// search was implausibly small. Two-thread models take three
/// preemptions and three-thread models two, except the ones
/// `contended!` declares and the ones whose comment says they were
/// shrunk: every model is sized to finish in under a minute.
/// A model whose three threads all contend for one lock or semaphore.
/// The normal run explores it at preemption bound 1; an `#[ignore]`d twin
/// explores a smaller scenario of its own at bound 2, nightly
/// (`just loom-sync-deep`). The shared scenario does not finish at bound 2
/// in any useful time (43 to 79 minutes a model, and 33 hours unfinished
/// for the upgradable read), so each twin keeps one thread from parking or
/// fixes the order two waiters arrive in, and finishes in under 10
/// minutes.
;
/// Drives `future` on this loom thread, parking it between polls until
/// its waker fires. A wakeup that never comes parks it for good, which
/// loom reports as a deadlock.
/// Raises `flag` once the wrapped future's first poll returned `Pending`,
/// so another thread can queue strictly behind it.
/// Polls `future` once with a waker that does nothing, then drops it.