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
//! `SlotPark` — a futex park for "wait until a bounded resource frees a slot".
//!
//! The lossless-backpressure primitive: when a bounded resource (a full ring, a
//! saturated pool, a closed window) can't admit a producer, the producer parks
//! here instead of dropping or busy-spinning; whoever frees a slot calls
//! [`wake_all`](SlotPark::wake_all). It is the wait/wake half of any
//! block-until-room design — decoupled from *what* the resource is, so a queue,
//! an arena, a rate limiter, or a connection pool can all reuse it. proxima's
//! telemetry recorder is the first consumer, not the only one.
//!
//! ## Why a futex, not a `Condvar`
//!
//! A `std::sync::Condvar` needs a `Mutex` companion and is std-only. This park
//! is a raw futex (`atomic-wait`: Linux `futex`, Windows `WaitOnAddress`, macOS
//! `__ulock_wait`) over a single `AtomicU32` epoch word — **mutex-free and
//! no_std** (it needs an OS for the syscall, but not Rust `std`). That matters
//! because the lossless guarantee it backs must be available on the hosted
//! no_std / kernel-bypass tiers, not just under `std`.
//!
//! It is behind the `park` feature (which pulls `atomic-wait`) and is therefore
//! **absent on bare-metal no-OS targets** — there is no futex without a kernel,
//! and "park a thread" is a category error with no scheduler. Those targets
//! spin, use a hardware wait, or an RTOS primitive instead.
//!
//! ## The epoch + the lost-wakeup window
//!
//! The single `AtomicU32` is a wake **epoch**, not the resource state. A waker
//! bumps it before waking; a parker snapshots it before the final resource
//! re-check and passes the snapshot to [`wait`](SlotPark::wait), which returns
//! immediately if the epoch already moved. That closes the classic lost-wakeup
//! window (a free that lands between the parker's last full-check and its park):
//! either the re-check sees the freed slot, or the epoch moved (wait returns at
//! once), or the parker is genuinely parked and the next free wakes it.
//!
//! The caller drives the loop (this type does not own the resource re-check),
//! in this exact order — announce, snapshot, RE-CHECK, wait:
//!
//! ```
//! use core::sync::atomic::{AtomicUsize, Ordering};
//! use proxima_core::park::SlotPark;
//!
//! // stand-in for any bounded resource: a permit counter, one permit free.
//! let permits = AtomicUsize::new(1);
//! let take_permit = || {
//! permits
//! .fetch_update(Ordering::AcqRel, Ordering::Acquire, |free| free.checked_sub(1))
//! .is_ok()
//! };
//! let park = SlotPark::new();
//!
//! loop {
//! if take_permit() {
//! break; // fast path: room now
//! }
//! let epoch = park.begin_wait(); // announce + snapshot
//! if take_permit() {
//! park.end_wait(); // RE-CHECK closes the window
//! break;
//! }
//! park.wait(epoch); // futex-park until the epoch moves
//! park.end_wait();
//! }
//!
//! assert_eq!(permits.load(Ordering::Acquire), 0, "the permit was consumed");
//! assert_eq!(park.waiters(), 0, "every begin_wait was paired with an end_wait");
//! ```
//!
//! `waiters` is a pure optimisation: [`wake_all`](SlotPark::wake_all) skips the
//! epoch bump + wake syscall entirely when it is zero (the steady state), so a
//! consumer that never exerts backpressure pays nothing.
use ;
/// A futex wait-for-a-freed-slot park. Shared by `&`, so many producers park on
/// one instance and any thread can [`wake_all`](Self::wake_all) them. See the
/// module docs for the caller's announce → snapshot → re-check → wait loop.