Skip to main content

compio_signal/unix/
mod.rs

1//! Unix-specific types for signal handling.
2
3use std::{io, sync::LazyLock};
4
5mod half_lock;
6
7use nix::sys::signal::{self, SigHandler, Signal};
8use slab::Slab;
9use synchrony::sync::async_flag::{AsyncFlag as Event, AsyncFlagHandle as EventHandle};
10
11use crate::unix::half_lock::HalfLock;
12
13static HANDLER: LazyLock<HalfLock<Slab<(Signal, EventHandle)>>> = LazyLock::new(HalfLock::default);
14
15extern "C" fn signal_handler(sig: i32) {
16    let Ok(sig) = Signal::try_from(sig) else {
17        return;
18    };
19    for handler in HANDLER
20        .read()
21        .iter()
22        .filter_map(|(_, (s, handler))| (sig == *s).then_some(handler))
23    {
24        handler.clone().notify();
25    }
26}
27
28fn register(sig: Signal, event: &Event) -> io::Result<usize> {
29    let handle = event.handle();
30    let mut guard = HANDLER.write();
31    let mut new = Slab::clone(&*guard);
32    let key = new.insert((sig, handle));
33    guard.store(new);
34    unsafe { signal::signal(sig, SigHandler::Handler(signal_handler)) }?;
35
36    Ok(key)
37}
38
39fn unregister(sig: Signal, key: usize) -> io::Result<()> {
40    let mut handler = HANDLER.write();
41    let mut new = Slab::clone(&*handler);
42    new.remove(key);
43    let need_uninit = new.iter().all(|(_, (s, _))| *s != sig);
44
45    if need_uninit {
46        unsafe { signal::signal(sig, SigHandler::SigDfl) }?;
47    }
48
49    handler.store(new);
50
51    Ok(())
52}
53
54/// A listener to unix signal event.
55#[derive(Debug)]
56struct SignalListener {
57    sig: Signal,
58    key: usize,
59    event: Option<Event>,
60}
61
62impl SignalListener {
63    fn new(sig: i32) -> io::Result<Self> {
64        let sig = Signal::try_from(sig)?;
65        let event = Event::new();
66        let key = register(sig, &event)?;
67        Ok(Self {
68            sig,
69            key,
70            event: Some(event),
71        })
72    }
73
74    async fn wait(mut self) {
75        self.event
76            .take()
77            .expect("event could not be None")
78            .wait()
79            .await
80    }
81}
82
83impl Drop for SignalListener {
84    fn drop(&mut self) {
85        _ = unregister(self.sig, self.key);
86    }
87}
88
89/// Creates a new listener which will receive notifications when the current
90/// process receives the specified signal.
91pub async fn signal(sig: i32) -> io::Result<()> {
92    let fd = SignalListener::new(sig)?;
93    fd.wait().await;
94    Ok(())
95}