Skip to main content

ntex_server/
signals.rs

1use std::{cell::RefCell, sync::Mutex, thread};
2
3use ntex_rt::System;
4
5use crate::server::Server;
6
7thread_local! {
8    static HANDLERS: RefCell<Vec<oneshot::Sender<Signal>>> = RefCell::default();
9}
10
11type CB = Box<dyn Fn(Signal) + Send>;
12static CUR_SYS: Mutex<RefCell<Option<(System, CB)>>> = Mutex::new(RefCell::new(None));
13
14/// Different types of process signals
15#[derive(PartialEq, Eq, Clone, Copy, Debug)]
16pub enum Signal {
17    /// SIGHUP
18    Hup,
19    /// SIGINT
20    Int,
21    /// SIGTERM
22    Term,
23    /// SIGQUIT
24    Quit,
25}
26
27/// Register signal handler.
28pub fn signal() -> oneshot::Receiver<Signal> {
29    let (tx, rx) = oneshot::channel();
30    System::current().arbiter().exec_fn(|| {
31        HANDLERS.with(|handlers| {
32            handlers.borrow_mut().push(tx);
33        });
34    });
35
36    rx
37}
38
39fn register_system<T: Send + 'static>(srv: Server<T>) -> bool {
40    let Ok(guard) = CUR_SYS.lock() else {
41        log::error!("Cannot lock mutex");
42        return true;
43    };
44
45    let mut sys = guard.borrow_mut();
46    let started = sys.is_some();
47    *sys = Some((System::current(), Box::new(move |sig| srv.signal(sig))));
48    started
49}
50
51fn handle_signal(sig: Signal) {
52    if let Ok(guard) = CUR_SYS.lock()
53        && let Some((sys, srv)) = &*guard.borrow()
54    {
55        (*srv)(sig);
56        sys.arbiter().exec_fn(move || {
57            HANDLERS.with(|handlers| {
58                for tx in handlers.borrow_mut().drain(..) {
59                    let _ = tx.send(sig);
60                }
61            });
62        });
63    }
64}
65
66#[cfg(target_family = "unix")]
67/// Register signal handler.
68///
69/// Signals are handled by oneshots, you have to re-register
70/// after each signal.
71pub(crate) fn start<T: Send + 'static>(srv: Server<T>) {
72    if !register_system(srv) {
73        let _ = thread::Builder::new()
74            .name("ntex-server signals".to_string())
75            .spawn(move || {
76                use signal_hook::consts::signal::{SIGHUP, SIGINT, SIGQUIT, SIGTERM};
77                use signal_hook::iterator::Signals;
78
79                let sigs = vec![SIGHUP, SIGINT, SIGTERM, SIGQUIT];
80                let mut signals = match Signals::new(sigs) {
81                    Ok(signals) => signals,
82                    Err(e) => {
83                        log::error!("Cannot initialize signals handler: {e}");
84                        return;
85                    }
86                };
87                for info in &mut signals {
88                    let sig = match info {
89                        SIGHUP => Signal::Hup,
90                        SIGTERM => Signal::Term,
91                        SIGINT => Signal::Int,
92                        SIGQUIT => Signal::Quit,
93                        _ => continue,
94                    };
95                    handle_signal(sig);
96
97                    if matches!(sig, Signal::Int | Signal::Quit) {
98                        return;
99                    }
100                }
101            });
102    }
103}
104
105#[cfg(target_family = "windows")]
106/// Register signal handler.
107///
108/// Signals are handled by oneshots, you have to re-register
109/// after each signal.
110pub(crate) fn start<T: Send + 'static>(srv: Server<T>) {
111    if !register_system(srv) {
112        let _ = thread::Builder::new()
113            .name("ntex-server signals".to_string())
114            .spawn(move || {
115                ctrlc::set_handler(|| handle_signal(Signal::Int))
116                    .expect("Error setting Ctrl-C handler");
117            });
118    }
119}