use core::task::{Context, Poll};
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, OnceLock};
use crate::sync::atomic_waker::AtomicWaker;
#[cfg(unix)]
pub mod unix;
#[cfg(windows)]
pub mod windows;
const SLOTS: usize = 65;
struct Slot {
count: AtomicU64,
dispatched: AtomicU64,
listeners: AtomicUsize,
}
static DELIVERIES: [Slot; SLOTS] = [const {
Slot {
count: AtomicU64::new(0),
dispatched: AtomicU64::new(0),
listeners: AtomicUsize::new(0),
}
}; SLOTS];
struct Waiter {
signum: usize,
waker: AtomicWaker,
}
fn waiters() -> &'static kovan_map::HashMap<u64, Arc<Waiter>> {
static WAITERS: OnceLock<kovan_map::HashMap<u64, Arc<Waiter>>> = OnceLock::new();
WAITERS.get_or_init(|| kovan_map::HashMap::with_capacity(64))
}
fn delivered(signum: usize) {
if let Some(slot) = DELIVERIES.get(signum) {
slot.count.fetch_add(1, Ordering::SeqCst);
}
}
fn dispatch() {
let mut moved = 0u128;
for (signum, slot) in DELIVERIES.iter().enumerate() {
let now = slot.count.load(Ordering::SeqCst);
if slot.dispatched.swap(now, Ordering::SeqCst) != now {
moved |= 1 << signum;
}
}
if moved == 0 {
return;
}
#[cfg(any(target_os = "linux", target_os = "android"))]
if moved & (1 << libc::SIGCHLD) != 0 {
crate::process::reap_fallback();
}
for (_, waiter) in waiters().iter() {
if moved & (1 << waiter.signum) != 0 {
waiter.waker.wake();
}
}
}
#[cfg(windows)]
fn listened(signum: usize) -> bool {
DELIVERIES
.get(signum)
.is_some_and(|slot| slot.listeners.load(Ordering::SeqCst) > 0)
}
struct Listener {
id: u64,
signum: usize,
seen: u64,
waiter: Arc<Waiter>,
}
impl Listener {
fn new(signum: usize) -> Self {
static NEXT: AtomicU64 = AtomicU64::new(0);
let id = NEXT.fetch_add(1, Ordering::Relaxed);
let slot = &DELIVERIES[signum];
slot.listeners.fetch_add(1, Ordering::SeqCst);
let waiter = Arc::new(Waiter {
signum,
waker: AtomicWaker::new(),
});
waiters().insert(id, Arc::clone(&waiter));
Self {
id,
signum,
seen: slot.count.load(Ordering::SeqCst),
waiter,
}
}
fn poll_recv(&mut self, cx: &mut Context<'_>, wait: impl FnOnce()) -> Poll<Option<()>> {
let count = &DELIVERIES[self.signum].count;
if self.take(count.load(Ordering::SeqCst)) {
return Poll::Ready(Some(()));
}
self.waiter.waker.register(cx.waker());
wait();
if self.take(count.load(Ordering::SeqCst)) {
return Poll::Ready(Some(()));
}
Poll::Pending
}
fn take(&mut self, now: u64) -> bool {
let moved = now != self.seen;
self.seen = now;
moved
}
}
impl core::fmt::Debug for Listener {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Listener")
.field("signum", &self.signum)
.finish_non_exhaustive()
}
}
impl Drop for Listener {
fn drop(&mut self) {
waiters().remove(&self.id);
DELIVERIES[self.signum]
.listeners
.fetch_sub(1, Ordering::SeqCst);
}
}
pub async fn ctrl_c() -> std::io::Result<()> {
#[cfg(unix)]
let mut listener = unix::signal(unix::SignalKind::interrupt())?;
#[cfg(windows)]
let mut listener = windows::ctrl_c()?;
listener.recv().await;
Ok(())
}
#[cfg(test)]
mod tests;