Skip to main content

ntex_rt/
signals.rs

1#![allow(static_mut_refs)]
2use std::{cell::RefCell, future::poll_fn, sync::Arc, task::Poll};
3
4use atomic_waker::AtomicWaker;
5
6use crate::System;
7
8thread_local! {
9    static STOP: RefCell<Option<oneshot::Sender<()>>> = const { RefCell::new(None) };
10    static HANDLERS: RefCell<Vec<oneshot::Sender<Arc<[Signal]>>>> = RefCell::default();
11}
12
13static mut CUR_SYS: Option<System> = None;
14static mut SIGS: [Option<Signal>; 10] = [None; 10];
15static HND_WAKER: AtomicWaker = AtomicWaker::new();
16
17/// Different types of process signals
18#[derive(PartialEq, Eq, Clone, Copy, Debug)]
19pub enum Signal {
20    /// SIGHUP
21    Hup,
22    /// SIGINT
23    Int,
24    /// SIGTERM
25    Term,
26    /// SIGQUIT
27    Quit,
28    /// SIGSEGV
29    Segv,
30}
31
32/// Register signal handler.
33///
34/// Signals are handled by oneshots, you have to re-register
35/// interest after each signal.
36pub fn signal() -> oneshot::AsyncReceiver<Arc<[Signal]>> {
37    let (tx, rx) = oneshot::async_channel();
38    System::current().handle().spawn(async move {
39        HANDLERS.with(|handlers| {
40            handlers.borrow_mut().push(tx);
41        });
42    });
43
44    rx
45}
46
47/// Check if signal handling is enabled.
48pub fn is_enabled() -> bool {
49    unsafe { CUR_SYS.is_some() }
50}
51
52fn register_system(sys: &System) -> bool {
53    unsafe {
54        if CUR_SYS.is_some() {
55            false
56        } else {
57            CUR_SYS = Some(sys.clone());
58
59            let (tx, rx) = oneshot::async_channel();
60            sys.handle().spawn(signals(rx));
61            STOP.with(|stop| {
62                *stop.borrow_mut() = Some(tx);
63            });
64            true
65        }
66    }
67}
68
69fn unregister_system(sys: &System) -> bool {
70    unsafe {
71        if let Some(cur) = CUR_SYS.take() {
72            if cur.id() == sys.id() {
73                sys.handle().spawn(async move {
74                    STOP.with(|stop| {
75                        if let Some(tx) = stop.borrow_mut().take() {
76                            let _ = tx.send(());
77                        }
78                    });
79                });
80                true
81            } else {
82                CUR_SYS = Some(cur);
83                false
84            }
85        } else {
86            false
87        }
88    }
89}
90
91fn handle_signal(sig: Signal) {
92    unsafe {
93        for s in &mut SIGS {
94            if s.is_none() {
95                *s = Some(sig);
96                break;
97            }
98        }
99        HND_WAKER.wake();
100    }
101}
102
103#[cfg(target_family = "unix")]
104static mut SIG_HANDLERS: [Option<signal_hook::SigId>; 10] = [None; 10];
105
106#[cfg(target_family = "unix")]
107/// Register signal handler.
108pub(crate) fn start(sys: &System) {
109    static ONCE: std::sync::Once = std::sync::Once::new();
110
111    if register_system(sys) {
112        use nix::sys::signal;
113        use signal_hook::consts::signal::{SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR2};
114        use signal_hook::low_level::register;
115
116        ONCE.call_once(|| {
117            // Use u128 for alignment.
118            let buf = Vec::leak(vec![0u128; 4096]);
119            let stack = libc::stack_t {
120                ss_sp: buf.as_ptr() as *mut libc::c_void,
121                ss_flags: 0,
122                ss_size: std::mem::size_of_val(buf),
123            };
124            let mut old = libc::stack_t {
125                ss_sp: std::ptr::null_mut(),
126                ss_flags: 0,
127                ss_size: 0,
128            };
129            let result = unsafe { libc::sigaltstack(&raw const stack, &raw mut old) };
130            if result != 0 {
131                log::error!("Cannot set signal stack");
132            }
133
134            let sig_action = signal::SigAction::new(
135                signal::SigHandler::Handler(sig_segv),
136                signal::SaFlags::SA_NODEFER | signal::SaFlags::SA_ONSTACK,
137                signal::SigSet::empty(),
138            );
139            unsafe {
140                if signal::sigaction(signal::SIGSEGV, &sig_action).is_err() {
141                    log::error!("Cannot install signal handler for SIGSEGV");
142                }
143                if signal::sigaction(signal::SIGABRT, &sig_action).is_err() {
144                    log::error!("Cannot install signal handler for SIGABRT");
145                }
146            }
147        });
148
149        for (idx, s, sig) in [
150            (0, SIGHUP, Signal::Hup),
151            (1, SIGINT, Signal::Int),
152            (2, SIGTERM, Signal::Term),
153            (3, SIGQUIT, Signal::Quit),
154        ] {
155            unsafe {
156                match register(s, move || handle_signal(sig)) {
157                    Ok(s) => SIG_HANDLERS[idx] = Some(s),
158                    Err(e) => {
159                        log::error!("Cannot install signal handler for {sig:?} with {e:?}");
160                    }
161                }
162            }
163        }
164
165        unsafe {
166            match register(SIGUSR2, || crate::system::sig_usr2()) {
167                Ok(s) => SIG_HANDLERS[5] = Some(s),
168                Err(_) => log::error!("Cannot install signal handler for SIGUSR2"),
169            }
170        }
171    }
172}
173
174#[cfg(target_family = "unix")]
175/// Unregister signal handler.
176pub(crate) fn stop(sys: &System) {
177    if unregister_system(sys) {
178        use signal_hook::low_level::unregister;
179
180        unsafe {
181            for sig in &mut SIG_HANDLERS {
182                if let Some(s) = sig.take() {
183                    let _ = unregister(s);
184                }
185            }
186        }
187    }
188}
189
190#[cfg(target_family = "windows")]
191/// Register signal handler.
192///
193/// Signals are handled by oneshots, you have to re-register
194/// after each signal.
195pub(crate) fn start(sys: &System) {
196    if register_system(sys) {
197        ctrlc::set_handler(move || handle_signal(Signal::Int))
198            .expect("Error setting Ctrl-C handler");
199    }
200}
201
202#[cfg(target_family = "windows")]
203/// Unregister signal handler.
204pub(crate) fn stop(sys: &System) {
205    if unregister_system(sys) {
206        log::info!("Signals handling is disabled");
207    }
208}
209
210async fn signals(rx: oneshot::AsyncReceiver<()>) {
211    let mut rx = std::pin::pin!(rx);
212
213    poll_fn(|cx| {
214        if rx.as_mut().poll(cx).is_ready() {
215            Poll::Ready(())
216        } else {
217            HND_WAKER.register(cx.waker());
218
219            let mut sigs = Vec::new();
220            unsafe {
221                for sig in &mut SIGS {
222                    if let Some(sig) = sig.take() {
223                        sigs.push(sig);
224                    }
225                }
226            }
227            if !sigs.is_empty() {
228                let sigs: Arc<[Signal]> = Arc::from(sigs);
229
230                HANDLERS.with(|handlers| {
231                    for tx in handlers.borrow_mut().drain(..) {
232                        let _ = tx.send(sigs.clone());
233                    }
234                });
235            }
236
237            Poll::Pending
238        }
239    })
240    .await;
241}
242
243#[cfg(target_family = "unix")]
244extern "C" fn sig_segv(_: i32) {
245    eprintln!("Stack Overflow:\n{:?}", backtrace::Backtrace::new());
246    handle_signal(Signal::Segv);
247}