#![doc(
html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png"
)]
cfg_if::cfg_if! {
if #[cfg(windows)] {
mod channel;
use channel as sys;
} else {
mod pipe;
use pipe as sys;
}
}
cfg_if::cfg_if! {
if #[cfg(unix)] {
use signal_hook_registry as registry;
} else if #[cfg(windows)] {
mod windows_registry;
use windows_registry as registry;
}
}
use futures_core::ready;
use futures_core::stream::Stream;
use registry::SigId;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
#[cfg(unix)]
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
mod signum {
pub(crate) use std::os::raw::c_int;
macro_rules! sig {
($rustix_name:ident, $raw_value:literal) => {{
#[cfg(unix)]
{
rustix::process::Signal::$rustix_name.as_raw()
}
#[cfg(windows)]
{
$raw_value
}
}};
}
pub const SIGHUP: c_int = sig!(HUP, 1);
pub const SIGINT: c_int = sig!(INT, 2);
pub const SIGQUIT: c_int = sig!(QUIT, 3);
pub const SIGILL: c_int = sig!(ILL, 4);
pub const SIGTRAP: c_int = sig!(TRAP, 5);
pub const SIGABRT: c_int = sig!(ABORT, 6);
pub const SIGFPE: c_int = sig!(FPE, 8);
pub const SIGKILL: c_int = sig!(KILL, 9);
pub const SIGSEGV: c_int = sig!(SEGV, 11);
pub const SIGPIPE: c_int = sig!(PIPE, 13);
pub const SIGALRM: c_int = sig!(ALARM, 14);
pub const SIGTERM: c_int = sig!(TERM, 15);
pub const SIGTTIN: c_int = sig!(TTIN, 21);
pub const SIGTTOU: c_int = sig!(TTOU, 22);
pub const SIGXCPU: c_int = sig!(XCPU, 24);
pub const SIGXFSZ: c_int = sig!(XFSZ, 25);
pub const SIGVTALRM: c_int = sig!(VTALARM, 26);
pub const SIGPROF: c_int = sig!(PROF, 27);
pub const SIGWINCH: c_int = sig!(WINCH, 28);
pub const SIGCHLD: c_int = sig!(CHILD, 17);
pub const SIGBUS: c_int = sig!(BUS, 7);
pub const SIGUSR1: c_int = sig!(USR1, 10);
pub const SIGUSR2: c_int = sig!(USR2, 12);
pub const SIGCONT: c_int = sig!(CONT, 18);
pub const SIGSTOP: c_int = sig!(STOP, 19);
pub const SIGTSTP: c_int = sig!(TSTP, 20);
pub const SIGURG: c_int = sig!(URG, 23);
#[cfg(not(target_os = "haiku"))]
pub const SIGIO: c_int = sig!(IO, 29);
pub const SIGSYS: c_int = sig!(SYS, 31);
}
macro_rules! define_signal_enum {
(
$(#[$outer:meta])*
pub enum Signal {
$(
$(#[cfg($($inner_cfg:tt)*)])?
$(#[doc $($inner_doc:tt)*])*
$name:ident = $value:ident,
)*
}
) => {
$(#[$outer])*
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[repr(i32)]
pub enum Signal {
$(
$(#[cfg($($inner_cfg)*)])?
$(#[doc $($inner_doc)*])*
$name = signum::$value,
)*
}
impl Signal {
fn number(self) -> std::os::raw::c_int {
match self {
$(
$(#[cfg($($inner_cfg)*)])?
Signal::$name => signum::$value,
)*
}
}
#[cfg(unix)]
fn from_number(number: std::os::raw::c_int) -> Option<Self> {
match number {
$(
$(#[cfg($($inner_cfg)*)])?
signum::$value => Some(Signal::$name),
)*
_ => None,
}
}
}
}
}
define_signal_enum! {
pub enum Signal {
Hup = SIGHUP,
Int = SIGINT,
Quit = SIGQUIT,
Ill = SIGILL,
Trap = SIGTRAP,
#[doc(alias = "Iot")]
#[doc(alias = "Abrt")]
Abort = SIGABRT,
Bus = SIGBUS,
Fpe = SIGFPE,
Kill = SIGKILL,
Usr1 = SIGUSR1,
Segv = SIGSEGV,
Usr2 = SIGUSR2,
Pipe = SIGPIPE,
#[doc(alias = "Alrm")]
Alarm = SIGALRM,
Term = SIGTERM,
#[doc(alias = "Chld")]
Child = SIGCHLD,
Cont = SIGCONT,
Stop = SIGSTOP,
Tstp = SIGTSTP,
Ttin = SIGTTIN,
Ttou = SIGTTOU,
Urg = SIGURG,
Xcpu = SIGXCPU,
Xfsz = SIGXFSZ,
#[doc(alias = "Vtalrm")]
Vtalarm = SIGVTALRM,
Prof = SIGPROF,
Winch = SIGWINCH,
#[cfg(not(target_os = "haiku"))]
#[doc(alias = "Poll")]
Io = SIGIO,
#[doc(alias = "Unused")]
Sys = SIGSYS,
}
}
pub struct Signals {
notifier: sys::Notifier,
signal_ids: HashMap<Signal, SigId>,
}
impl Drop for Signals {
fn drop(&mut self) {
for signal in self.signal_ids.values() {
registry::unregister(*signal);
}
}
}
impl fmt::Debug for Signals {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct RegisteredSignals<'a>(&'a HashMap<Signal, SigId>);
impl fmt::Debug for RegisteredSignals<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self.0.keys()).finish()
}
}
f.debug_struct("Signals")
.field("notifier", &self.notifier)
.field("signal_ids", &RegisteredSignals(&self.signal_ids))
.finish()
}
}
impl Signals {
pub fn new<B>(signals: impl IntoIterator<Item = B>) -> io::Result<Self>
where
B: Borrow<Signal>,
{
let mut this = Self {
notifier: sys::Notifier::new()?,
signal_ids: HashMap::new(),
};
this.add_signals(signals)?;
Ok(this)
}
pub fn add_signals<B>(&mut self, signals: impl IntoIterator<Item = B>) -> io::Result<()>
where
B: Borrow<Signal>,
{
for signal in signals {
let signal = signal.borrow();
if self.signal_ids.contains_key(signal) {
continue;
}
let closure = self.notifier.add_signal(*signal)?;
let id = unsafe {
registry::register(signal.number(), closure)?
};
self.signal_ids.insert(*signal, id);
}
Ok(())
}
pub fn remove_signals<B>(&mut self, signals: impl IntoIterator<Item = B>) -> io::Result<()>
where
B: Borrow<Signal>,
{
for signal in signals {
let signal = signal.borrow();
let id = match self.signal_ids.remove(signal) {
Some(id) => id,
None => continue,
};
self.notifier.remove_signal(*signal)?;
registry::unregister(id);
}
Ok(())
}
}
#[cfg(unix)]
impl AsRawFd for Signals {
fn as_raw_fd(&self) -> RawFd {
self.notifier.as_raw_fd()
}
}
#[cfg(unix)]
impl AsFd for Signals {
fn as_fd(&self) -> BorrowedFd<'_> {
self.notifier.as_fd()
}
}
impl Unpin for Signals {}
impl Stream for Signals {
type Item = io::Result<Signal>;
#[inline]
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Pin::new(&mut &*self).poll_next(cx)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, None)
}
}
impl Stream for &Signals {
type Item = io::Result<Signal>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let signal = ready!(self.notifier.poll_next(cx))?;
Poll::Ready(Some(Ok(signal)))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, None)
}
}