#[cfg(unix)]
mod unix;
#[cfg(windows)]
mod windows;
use std::{
any::Any,
fmt,
future::poll_fn,
io,
panic::{self, AssertUnwindSafe},
pin::Pin,
sync::{Mutex, MutexGuard, PoisonError},
task::{Context, Poll, Waker},
};
use futures_core::Stream;
#[cfg(unix)]
use self::unix::{Handlers, Watch, arrived};
#[cfg(all(test, unix))]
pub(crate) use self::unix::{fill_pipe, handle};
#[cfg(all(test, windows))]
pub(crate) use self::windows::handle;
#[cfg(windows)]
use self::windows::{Handlers, Watch, arrived};
use crate::{Local, Mode, Runtime, scheduler::dispose};
pub struct Signals<M = Local>
where
M: Mode,
{
signals: Set,
key: usize,
watch: Watch<M>,
}
impl<M> Signals<M>
where
M: Mode,
{
pub fn new<I>(runtime: &Runtime<M>, signals: I) -> io::Result<Self>
where
I: IntoIterator<Item = Signal>,
{
let signals = signals.into_iter().collect();
let watch = Watch::new(runtime)?;
let (added, woken) = listen(signals);
let created = added.map(|key| Self {
signals,
key,
watch,
});
if let Some(panic) = wake(woken.into_iter().flatten()) {
panic::resume_unwind(panic);
}
created
}
pub async fn recv(&mut self) -> io::Result<Signal> {
poll_fn(|cx| self.poll_recv(cx)).await
}
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Signal>> {
let delivered = loop {
match self.watch.poll_deliver(cx) {
Poll::Ready(Ok(())) => {}
Poll::Ready(Err(e)) => break Err(e),
Poll::Pending => break Ok(()),
}
};
if let Some(signal) = take(self.key, cx.waker()) {
return Poll::Ready(Ok(signal));
}
match delivered {
Ok(()) => Poll::Pending,
Err(e) => Poll::Ready(Err(e)),
}
}
}
impl<M> fmt::Debug for Signals<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Signals")
.field("signals", &self.signals)
.finish_non_exhaustive()
}
}
impl<M> Stream for Signals<M>
where
M: Mode,
{
type Item = io::Result<Signal>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.get_mut().poll_recv(cx).map(Some)
}
}
impl<M> Drop for Signals<M>
where
M: Mode,
{
fn drop(&mut self) {
unlisten(self.key);
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Signal {
Interrupt,
#[cfg(unix)]
Terminate,
#[cfg(unix)]
Hangup,
#[cfg(unix)]
Quit,
#[cfg(unix)]
User1,
#[cfg(unix)]
User2,
#[cfg(unix)]
Child,
#[cfg(unix)]
WindowChange,
#[cfg(windows)]
Break,
}
impl Signal {
const ALL: &[Signal] = &[
Signal::Interrupt,
#[cfg(unix)]
Signal::Terminate,
#[cfg(unix)]
Signal::Hangup,
#[cfg(unix)]
Signal::Quit,
#[cfg(unix)]
Signal::User1,
#[cfg(unix)]
Signal::User2,
#[cfg(unix)]
Signal::Child,
#[cfg(unix)]
Signal::WindowChange,
#[cfg(windows)]
Signal::Break,
];
}
const _: () = {
assert!(Signal::ALL.len() <= u32::BITS as usize);
let mut index = 0;
while index < Signal::ALL.len() {
assert!(Signal::ALL[index] as usize == index);
index += 1;
}
};
fn listen(signals: Set) -> (io::Result<usize>, Option<Vec<Waker>>) {
let mut registry = lock();
let woken = registry.deliver(arrived());
(registry.add(signals), woken)
}
fn wake<I>(wakers: I) -> Option<Box<dyn Any + Send>>
where
I: IntoIterator<Item = Waker>,
{
let mut first_panic = None;
for waker in wakers {
if let Err(panic) = panic::catch_unwind(AssertUnwindSafe(|| waker.wake())) {
match first_panic {
None => first_panic = Some(panic),
Some(_) => dispose(panic),
}
}
}
first_panic
}
fn unlisten(key: usize) {
let removed = {
let mut registry = lock();
let removed = registry.listeners.get_mut(key).and_then(Option::take);
if let Some(listener) = &removed {
for signal in listener.signals.difference(registry.listened()).iter() {
registry.handlers.give_back(signal);
}
}
removed
};
drop(removed);
}
fn take(key: usize, waker: &Waker) -> Option<Signal> {
let waker = waker.clone();
let replaced = {
let mut registry = lock();
let Some(Some(listener)) = registry.listeners.get_mut(key) else {
unreachable!("a listener stays in the registry for as long as its `Signals` lives");
};
if let Some(signal) = listener.arrived.first() {
listener.arrived = listener.arrived.difference(signal.into());
return Some(signal);
}
listener.waker.replace(waker)
};
drop(replaced);
None
}
fn lock() -> MutexGuard<'static, Registry> {
REGISTRY.lock().unwrap_or_else(PoisonError::into_inner)
}
static REGISTRY: Mutex<Registry> = Mutex::new(Registry {
listeners: Vec::new(),
handlers: Handlers::new(),
});
struct Registry {
listeners: Vec<Option<Listener>>,
handlers: Handlers,
}
impl Registry {
fn add(&mut self, signals: Set) -> io::Result<usize> {
let mut taken = Set::EMPTY;
for signal in signals.difference(self.listened()).iter() {
if let Err(e) = self.handlers.take(signal) {
for signal in taken.iter() {
self.handlers.give_back(signal);
}
return Err(e);
}
taken = taken.union(signal.into());
}
let listener = Some(Listener {
signals,
arrived: Set::EMPTY,
waker: None,
});
let key = match self.listeners.iter().position(Option::is_none) {
Some(key) => {
self.listeners[key] = listener;
key
}
None => {
self.listeners.push(listener);
self.listeners.len() - 1
}
};
Ok(key)
}
fn deliver(&mut self, arrived: Set) -> Option<Vec<Waker>> {
let mut woken = None;
for listener in self.listeners.iter_mut().flatten() {
let sent = arrived.intersection(listener.signals);
if sent.is_empty() {
continue;
}
listener.arrived = listener.arrived.union(sent);
woken
.get_or_insert_with(Vec::new)
.extend(listener.waker.take());
}
woken
}
fn listened(&self) -> Set {
self.listeners
.iter()
.flatten()
.fold(Set::EMPTY, |listened, listener| {
listened.union(listener.signals)
})
}
}
struct Listener {
signals: Set,
arrived: Set,
waker: Option<Waker>,
}
#[derive(Clone, Copy, Eq, PartialEq)]
struct Set(u32);
impl Set {
const EMPTY: Self = Self(0);
fn is_empty(self) -> bool {
self.0 == 0
}
fn contains(self, signal: Signal) -> bool {
!self.intersection(signal.into()).is_empty()
}
fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
fn intersection(self, other: Self) -> Self {
Self(self.0 & other.0)
}
fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
fn first(self) -> Option<Signal> {
self.iter().next()
}
fn iter(self) -> impl Iterator<Item = Signal> {
Signal::ALL
.iter()
.copied()
.filter(move |signal| self.contains(*signal))
}
}
impl fmt::Debug for Set {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self.iter()).finish()
}
}
impl From<Signal> for Set {
fn from(signal: Signal) -> Self {
Self(1 << signal as u32)
}
}
impl FromIterator<Signal> for Set {
fn from_iter<I>(signals: I) -> Self
where
I: IntoIterator<Item = Signal>,
{
signals
.into_iter()
.fold(Self::EMPTY, |set, signal| set.union(signal.into()))
}
}