1use core::task::{Context, Poll};
19use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
20use std::sync::{Arc, OnceLock};
21
22use crate::sync::atomic_waker::AtomicWaker;
23
24#[cfg(unix)]
25pub mod unix;
26#[cfg(windows)]
27pub mod windows;
28
29const SLOTS: usize = 65;
32
33struct Slot {
35 count: AtomicU64,
37 dispatched: AtomicU64,
39 listeners: AtomicUsize,
41}
42
43static DELIVERIES: [Slot; SLOTS] = [const {
44 Slot {
45 count: AtomicU64::new(0),
46 dispatched: AtomicU64::new(0),
47 listeners: AtomicUsize::new(0),
48 }
49}; SLOTS];
50
51struct Waiter {
53 signum: usize,
54 waker: AtomicWaker,
55}
56
57fn waiters() -> &'static kovan_map::HashMap<u64, Arc<Waiter>> {
59 static WAITERS: OnceLock<kovan_map::HashMap<u64, Arc<Waiter>>> = OnceLock::new();
60 WAITERS.get_or_init(|| kovan_map::HashMap::with_capacity(64))
62}
63
64fn delivered(signum: usize) {
67 if let Some(slot) = DELIVERIES.get(signum) {
68 slot.count.fetch_add(1, Ordering::SeqCst);
69 }
70}
71
72fn dispatch() {
75 let mut moved = 0u128;
76 for (signum, slot) in DELIVERIES.iter().enumerate() {
77 let now = slot.count.load(Ordering::SeqCst);
78 if slot.dispatched.swap(now, Ordering::SeqCst) != now {
79 moved |= 1 << signum;
80 }
81 }
82 if moved == 0 {
83 return;
84 }
85 #[cfg(any(target_os = "linux", target_os = "android"))]
86 if moved & (1 << libc::SIGCHLD) != 0 {
87 crate::process::reap_fallback();
88 }
89 for (_, waiter) in waiters().iter() {
90 if moved & (1 << waiter.signum) != 0 {
91 waiter.waker.wake();
92 }
93 }
94}
95
96#[cfg(windows)]
98fn listened(signum: usize) -> bool {
99 DELIVERIES
100 .get(signum)
101 .is_some_and(|slot| slot.listeners.load(Ordering::SeqCst) > 0)
102}
103
104struct Listener {
106 id: u64,
107 signum: usize,
108 seen: u64,
109 waiter: Arc<Waiter>,
110}
111
112impl Listener {
113 fn new(signum: usize) -> Self {
116 static NEXT: AtomicU64 = AtomicU64::new(0);
117 let id = NEXT.fetch_add(1, Ordering::Relaxed);
118 let slot = &DELIVERIES[signum];
119 slot.listeners.fetch_add(1, Ordering::SeqCst);
120 let waiter = Arc::new(Waiter {
121 signum,
122 waker: AtomicWaker::new(),
123 });
124 waiters().insert(id, Arc::clone(&waiter));
125 Self {
126 id,
127 signum,
128 seen: slot.count.load(Ordering::SeqCst),
129 waiter,
130 }
131 }
132
133 fn poll_recv(&mut self, cx: &mut Context<'_>, wait: impl FnOnce()) -> Poll<Option<()>> {
136 let count = &DELIVERIES[self.signum].count;
137 if self.take(count.load(Ordering::SeqCst)) {
138 return Poll::Ready(Some(()));
139 }
140 self.waiter.waker.register(cx.waker());
141 wait();
142 if self.take(count.load(Ordering::SeqCst)) {
143 return Poll::Ready(Some(()));
144 }
145 Poll::Pending
146 }
147
148 fn take(&mut self, now: u64) -> bool {
149 let moved = now != self.seen;
150 self.seen = now;
151 moved
152 }
153}
154
155impl core::fmt::Debug for Listener {
156 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157 f.debug_struct("Listener")
158 .field("signum", &self.signum)
159 .finish_non_exhaustive()
160 }
161}
162
163impl Drop for Listener {
164 fn drop(&mut self) {
165 waiters().remove(&self.id);
166 DELIVERIES[self.signum]
167 .listeners
168 .fetch_sub(1, Ordering::SeqCst);
169 }
170}
171
172pub async fn ctrl_c() -> std::io::Result<()> {
181 #[cfg(unix)]
182 let mut listener = unix::signal(unix::SignalKind::interrupt())?;
183 #[cfg(windows)]
184 let mut listener = windows::ctrl_c()?;
185 listener.recv().await;
186 Ok(())
187}
188
189#[cfg(test)]
190mod tests;