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
//! Futex-based parking.
use Instant;
use crateFutex;
use crate;
use crateCachePadded;
/// A set of parked threads waiting for a queue condition to become true.
///
/// Waiters sleep on a 32-bit `epoch` word. Notifiers bump the word, then wake.
/// The kernel's compare-and-sleep takes over the role the mutex plays in the
/// fallback implementation: a notification that lands between a waiter's
/// re-check and its sleep has already changed the word, so the sleep does not
/// happen.
///
/// # Protocol
///
/// Waiter (`wait_until`):
///
/// 1. register: `waiters += 1`;
/// 2. `seen = epoch.load(Acquire)` — **before** the re-check;
/// 3. re-check the queue with an `AcqRel` RMW on the notifier's word;
/// 4. if not ready: `sleepers += 1`, `fence(SeqCst)`, `futex_wait(epoch,
/// seen)`, `sleepers -= 1`; then go to 2.
///
/// Notifier (`notify_one`), after its `AcqRel` CAS on that word:
///
/// 1. if `waiters == 0` (`Relaxed`), stop: this is the hot path;
/// 2. `epoch.fetch_add(1, Release)`;
/// 3. `fence(SeqCst)`, then if `sleepers != 0`, `futex_wake(epoch, 1)`.
///
/// # Why no wakeup is lost
///
/// The waiter's re-check (W3) and the notifier's CAS (N1) are RMWs on the same
/// word, so one precedes the other in its modification order.
///
/// * N1 first: W3 reads it (an RMW reads the latest value); the waiter sees
/// the new state and does not sleep.
/// * W3 first: N1 reads from W3's release sequence and synchronises with it.
/// So registration happens-before the notifier's `waiters` load, which sees
/// it, and the notifier bumps. Also, `seen` (W2) happens-before the bump, so
/// by read-write coherence `seen` is an older epoch than the bump's. When
/// the waiter's `futex_wait` compares, either the bump is visible and it
/// returns at once, or the waiter is already asleep and the wake reaches it.
///
/// # Skipping the system call
///
/// A registered waiter is often still awake: spinning, re-checking, or just
/// woken by an earlier notification. Waking the futex anyway costs a system
/// call per queue operation, which measurably cut throughput under churn.
/// So the notifier wakes only if `sleepers != 0`.
///
/// That creates a second store-buffering race: the notifier writes `epoch`
/// then reads `sleepers`; the waiter writes `sleepers` then (in the kernel)
/// reads `epoch`. The two `SeqCst` fences sit in the single total order of
/// fences. If the waiter's fence is first, the notifier's `sleepers` load
/// sees the increment and wakes. If the notifier's fence is first, the
/// kernel's compare sees the bump and does not sleep. Both fences are on the
/// slow path; a notifier only reaches its fence when a thread is registered.
///
/// `close` bumps unconditionally and wakes everyone. Its `Release` bump pairs
/// with the waiter's `Acquire` read of `seen`, so after the waiter's next
/// iteration it sees the closed flag even through `Relaxed` loads.
///
/// The epoch is 32 bits. A waiter could sleep wrongly only if exactly a
/// multiple of 2^32 bumps landed between its read of `seen` and the kernel's
/// compare, a window of a few instructions; even then the next notification
/// wakes it. Rust's own futex `Condvar` accepts the same window.
///
/// Every other return from `futex_wait`, whether a timeout, a signal, a wake
/// meant for another waiter or a spurious one, is harmless: the loop re-reads
/// the epoch and re-checks.
pub
/// Both fields are cold while nobody is parked, so they share a line.