1#[cfg(feature = "loom")]
26use loom::sync::Mutex;
27#[cfg(feature = "loom")]
28use loom::sync::atomic::{AtomicBool, Ordering};
29use std::sync::{Arc, OnceLock};
30#[cfg(not(feature = "loom"))]
31use std::sync::Mutex;
32#[cfg(not(feature = "loom"))]
33use std::sync::atomic::{AtomicBool, Ordering};
34use std::task::{Context, Poll, Waker};
35
36use core::future::Future;
37use core::pin::Pin;
38
39use futures::task::AtomicWaker;
40use smallvec::SmallVec;
41
42struct WakerSlot(AtomicWaker);
52
53impl WakerSlot {
54 fn new() -> Self {
55 Self(AtomicWaker::new())
56 }
57
58 fn register(&self, waker: &Waker) {
59 self.0.register(waker);
60 }
61
62 fn wake(&self) {
63 self.0.wake();
64 }
65}
66
67type WaiterRegistry = SmallVec<[Arc<WakerSlot>; crate::sized::SIGNAL_WAITERS_INLINE_CAP]>;
74
75struct Level {
76 fired: AtomicBool,
77 waiters: Mutex<WaiterRegistry>,
78}
79
80impl Level {
81 fn new() -> Arc<Self> {
82 Arc::new(Self {
83 fired: AtomicBool::new(false),
84 waiters: Mutex::new(SmallVec::new()),
85 })
86 }
87
88 fn waiters(&self) -> impl core::ops::DerefMut<Target = WaiterRegistry> + '_ {
93 self.waiters.lock().unwrap_or_else(|err| err.into_inner())
94 }
95
96 fn fire(&self) {
97 if self.fired.swap(true, Ordering::AcqRel) {
98 return;
99 }
100 let drained = core::mem::take(&mut *self.waiters());
101 for slot in drained {
102 slot.wake();
103 }
104 }
105
106 fn is_fired(&self) -> bool {
107 self.fired.load(Ordering::Acquire)
108 }
109}
110
111#[derive(Clone)]
118pub struct Signal {
119 levels: Arc<[Arc<Level>]>,
120}
121
122impl Signal {
123 #[must_use]
124 pub fn new() -> Self {
125 Self {
126 levels: Arc::from([Level::new()]),
127 }
128 }
129
130 #[must_use]
133 pub fn child(&self) -> Self {
134 let mut levels = Vec::with_capacity(self.levels.len() + 1);
135 levels.extend(self.levels.iter().cloned());
136 levels.push(Level::new());
137 Self {
138 levels: Arc::from(levels),
139 }
140 }
141
142 pub fn fire(&self) {
145 if let Some(own) = self.levels.last() {
146 own.fire();
147 }
148 }
149
150 #[must_use]
152 pub fn is_fired(&self) -> bool {
153 self.levels.iter().any(|level| level.is_fired())
154 }
155
156 #[must_use]
159 pub fn fired(&self) -> Fired {
160 Fired {
161 levels: self.levels.clone(),
162 slot: OnceLock::new(),
163 }
164 }
165
166 #[must_use]
169 pub fn guard(self) -> SignalGuard {
170 SignalGuard {
171 signal: self,
172 armed: true,
173 }
174 }
175}
176
177impl Default for Signal {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183impl core::fmt::Debug for Signal {
184 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
185 formatter
186 .debug_struct("Signal")
187 .field("depth", &self.levels.len())
188 .field("fired", &self.is_fired())
189 .finish()
190 }
191}
192
193pub struct Fired {
196 levels: Arc<[Arc<Level>]>,
197 slot: OnceLock<Arc<WakerSlot>>,
198}
199
200impl Fired {
201 pub fn poll_shared(&self, cx: &mut Context<'_>) -> Poll<()> {
208 let slot = self.slot.get_or_init(|| {
212 let slot = Arc::new(WakerSlot::new());
213 for level in self.levels.iter() {
214 level.waiters().push(slot.clone());
215 }
216 slot
217 });
218 slot.register(cx.waker());
219 if self.levels.iter().any(|level| level.is_fired()) {
220 Poll::Ready(())
221 } else {
222 Poll::Pending
223 }
224 }
225}
226
227impl Future for Fired {
228 type Output = ();
229
230 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
231 self.poll_shared(cx)
232 }
233}
234
235impl Drop for Fired {
236 fn drop(&mut self) {
237 let Some(slot) = self.slot.take() else {
238 return;
239 };
240 for level in self.levels.iter() {
241 level
242 .waiters()
243 .retain(|candidate| !Arc::ptr_eq(candidate, &slot));
244 }
245 }
246}
247
248pub struct SignalGuard {
250 signal: Signal,
251 armed: bool,
252}
253
254impl SignalGuard {
255 #[must_use]
256 pub fn signal(&self) -> &Signal {
257 &self.signal
258 }
259
260 pub fn disarm(mut self) -> Signal {
263 self.armed = false;
264 self.signal.clone()
265 }
266}
267
268impl Drop for SignalGuard {
269 fn drop(&mut self) {
270 if self.armed {
271 self.signal.fire();
272 }
273 }
274}
275
276#[cfg(all(test, not(feature = "loom")))]
282#[allow(clippy::unwrap_used, clippy::expect_used)]
283mod tests {
284 use std::panic::{AssertUnwindSafe, catch_unwind};
285 use std::sync::atomic::AtomicUsize;
286 use std::task::Wake;
287
288 use super::*;
289
290 struct CountingWaker(AtomicUsize);
291
292 impl Wake for CountingWaker {
293 fn wake(self: Arc<Self>) {
294 self.0.fetch_add(1, Ordering::Relaxed);
295 }
296 }
297
298 fn counting_waker() -> (Arc<CountingWaker>, Waker) {
299 let counter = Arc::new(CountingWaker(AtomicUsize::new(0)));
300 let waker = Waker::from(counter.clone());
301 (counter, waker)
302 }
303
304 #[test]
305 fn fire_is_sticky_and_idempotent() {
306 let signal = Signal::new();
307 assert!(!signal.is_fired());
308 signal.fire();
309 signal.fire();
310 assert!(signal.is_fired());
311 }
312
313 #[test]
314 fn late_observer_resolves_on_first_poll() {
315 let signal = Signal::new();
316 signal.fire();
317 let mut fired = signal.fired();
318 let (_, waker) = counting_waker();
319 let mut cx = Context::from_waker(&waker);
320 assert_eq!(Pin::new(&mut fired).poll(&mut cx), Poll::Ready(()));
321 }
322
323 #[test]
324 fn registered_waiter_is_woken_by_fire() {
325 let signal = Signal::new();
326 let mut fired = signal.fired();
327 let (counter, waker) = counting_waker();
328 let mut cx = Context::from_waker(&waker);
329 assert_eq!(Pin::new(&mut fired).poll(&mut cx), Poll::Pending);
330 signal.fire();
331 assert_eq!(counter.0.load(Ordering::Relaxed), 1);
332 assert_eq!(Pin::new(&mut fired).poll(&mut cx), Poll::Ready(()));
333 }
334
335 #[test]
336 fn child_observes_parent_fire() {
337 let parent = Signal::new();
338 let child = parent.child();
339 parent.fire();
340 assert!(child.is_fired());
341 }
342
343 #[test]
344 fn parent_is_untouched_by_child_fire() {
345 let parent = Signal::new();
346 let child = parent.child();
347 child.fire();
348 assert!(child.is_fired());
349 assert!(!parent.is_fired());
350 }
351
352 #[test]
353 fn grandchild_observes_root_through_the_merge() {
354 let root = Signal::new();
355 let grandchild = root.child().child();
356 root.fire();
357 assert!(grandchild.is_fired());
358 }
359
360 #[test]
361 fn guard_fires_own_level_on_drop() {
362 let signal = Signal::new();
363 let observer = signal.clone();
364 {
365 let _guard = signal.guard();
366 assert!(!observer.is_fired());
367 }
368 assert!(observer.is_fired());
369 }
370
371 #[test]
372 fn disarmed_guard_does_not_fire_on_drop() {
373 let signal = Signal::new();
374 let observer = signal.clone();
375 let guard = signal.guard();
376 let returned = guard.disarm();
377 assert!(!observer.is_fired());
378 assert!(!returned.is_fired());
379 }
380
381 #[test]
382 fn dropping_a_pending_waiter_unregisters_its_slot() {
383 let signal = Signal::new();
384 let (_, waker) = counting_waker();
385 let mut cx = Context::from_waker(&waker);
386 {
387 let mut fired = signal.fired();
388 assert_eq!(Pin::new(&mut fired).poll(&mut cx), Poll::Pending);
389 assert_eq!(signal.levels[0].waiters.lock().unwrap().len(), 1);
390 }
391 assert_eq!(signal.levels[0].waiters.lock().unwrap().len(), 0);
392 }
393
394 #[test]
398 fn a_poisoned_waiter_registry_still_wakes_its_waiters() {
399 let signal = Signal::new();
400 let mut fired = signal.fired();
401 let (counter, waker) = counting_waker();
402 let mut cx = Context::from_waker(&waker);
403 assert_eq!(Pin::new(&mut fired).poll(&mut cx), Poll::Pending);
404
405 let poisoner = signal.clone();
406 let outcome = catch_unwind(AssertUnwindSafe(|| {
407 let _held = poisoner.levels[0].waiters.lock().expect("uncontended lock");
408 panic!("poison the waiter registry");
409 }));
410 assert!(outcome.is_err(), "the poisoning closure must have unwound");
411 assert!(signal.levels[0].waiters.is_poisoned());
412
413 signal.fire();
414
415 assert_eq!(
416 counter.0.load(Ordering::Relaxed),
417 1,
418 "a poisoned registry must still deliver the wakeup"
419 );
420 assert_eq!(Pin::new(&mut fired).poll(&mut cx), Poll::Ready(()));
421 }
422
423 #[test]
424 fn repolling_does_not_grow_the_waiter_list() {
425 let signal = Signal::new();
426 let mut fired = signal.fired();
427 let (_, waker) = counting_waker();
428 let mut cx = Context::from_waker(&waker);
429 for _ in 0..64 {
430 assert_eq!(Pin::new(&mut fired).poll(&mut cx), Poll::Pending);
431 }
432 assert_eq!(signal.levels[0].waiters.lock().unwrap().len(), 1);
433 }
434}