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
//! Shared internal waiter list used by every primitive in [`super`].
//!
//! Lock-free, built on [`crate::lockfree::BoundedMpmcQueue`] (Vyukov's
//! ring-buffer algorithm — the same one this crate's `sync::mpsc` uses
//! for its own message buffer, independently stress-tested there) for
//! genuine FIFO delivery, with [`crate::lockfree::MpmcStack`] (proven
//! correct elsewhere in this crate) as a rare overflow fallback beyond
//! [`READY_CAPACITY`] concurrent registrations.
//!
//! # Why FIFO, not LIFO
//!
//! An earlier version of this module used a LIFO stack for the main
//! waiter list, on the reasoning that "no primitive here promises
//! fairness about *which* waiter wakes next" — true in isolation, but
//! LIFO has a much sharper failure mode than mere unfairness under
//! *sustained* contention from a fixed, small set of repeatedly-blocking
//! participants (a bounded `mpsc` channel's producer threads are exactly
//! this): each new registration can jump the queue ahead of an older one,
//! so if the same handful of contenders keep re-registering, one specific
//! unlucky waiter can be buried under an endless stream of newer
//! registrations and never surface — not just "served out of order" but
//! **starved indefinitely** once external activity quiets down (nothing
//! is left to keep digging through the pile via further `wake_one`
//! calls). This was measured as a real, reproducible deadlock: a
//! multi-producer stress test against `sync::mpsc`'s bounded channel
//! would have most producer threads finish all their sends while one
//! specific thread never made another byte of progress, parked forever.
//! FIFO closes this — the *oldest* registration is always the next one
//! popped, so no waiter can be perpetually skipped by newer arrivals.
//!
//! # Why cancellable
//!
//! Every primitive in this module follows a "check state; if not ready,
//! register; check state again; if *that* check also succeeds, proceed
//! without waiting" pattern (necessary to avoid a lost-wakeup race — see
//! e.g. `Mutex::poll_lock`'s comment). That second check succeeding is
//! the *common* case under real contention, not a rare one, and every
//! time it happens the registration from the first step would otherwise
//! sit around as dead weight forever (nothing re-polls that exact
//! task-already-succeeded path again to remove it). [`WaitQueue::cancel`]
//! marks it tombstoned instead; [`WaitQueue::wake_one`]/[`WaitQueue::wake_all`]
//! skip tombstoned entries when they're popped rather than wasting a real
//! wake on a task that already made progress on its own.
use crate;
use ;
use ;
use Waker;
/// Fixed capacity of the FIFO ring every `WaitQueue` allocates on its
/// first registration (see `ready`'s field doc for why this is lazy, not
/// eager). Comfortably above realistic concurrent-waiter counts for any
/// single primitive; registrations beyond this rare capacity fall back to
/// a heap-allocating (and LIFO, so best-effort-fair only) overflow stack —
/// see the module doc's "why FIFO" section for why exceeding this in
/// practice is the scenario worth avoiding, not routing through cleanly.
const READY_CAPACITY: usize = 256;
/// One registration: a `Waker` plus its own tombstone flag. See the
/// module doc's "why cancellable" section.
/// Returned by [`WaitQueue::register`]; hand back to [`WaitQueue::cancel`]
/// if the caller ends up not needing to wait after all.
;