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>>> = Default::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 guard = match CUR_SYS.lock() {
41        Ok(guard) => guard,
42        Err(_) => {
43            log::error!("Cannot lock mutex");
44            return true;
45        }
46    };
47
48    let mut sys = guard.borrow_mut();
49    let started = sys.is_some();
50    *sys = Some((System::current(), Box::new(move |sig| srv.signal(sig))));
51    started
52}
53
54fn handle_signal(sig: Signal) {
55    if let Ok(guard) = CUR_SYS.lock() {
56        if let Some((sys, srv)) = &*guard.borrow() {
57            (*srv)(sig);
58            sys.arbiter().exec_fn(move || {
59                HANDLERS.with(|handlers| {
60                    for tx in handlers.borrow_mut().drain(..) {
61                        let _ = tx.send(sig);
62                    }
63                })
64            });
65        }
66    }
67}
68
69#[cfg(target_family = "unix")]
70/// Register signal handler.
71///
72/// Signals are handled by oneshots, you have to re-register
73/// after each signal.
74pub(crate) fn start<T: Send + 'static>(srv: Server<T>) {
75    if !register_system(srv) {
76        let _ = thread::Builder::new()
77            .name("ntex-server signals".to_string())
78            .spawn(move || {
79                use signal_hook::consts::signal::*;
80                use signal_hook::iterator::Signals;
81
82                let sigs = vec![SIGHUP, SIGINT, SIGTERM, SIGQUIT];
83                let mut signals = match Signals::new(sigs) {
84                    Ok(signals) => signals,
85                    Err(e) => {
86                        log::error!("Cannot initialize signals handler: {e}");
87                        return;
88                    }
89                };
90                for info in &mut signals {
91                    let sig = match info {
92                        SIGHUP => Signal::Hup,
93                        SIGTERM => Signal::Term,
94                        SIGINT => Signal::Int,
95                        SIGQUIT => Signal::Quit,
96                        _ => continue,
97                    };
98                    handle_signal(sig);
99
100                    if matches!(sig, Signal::Int | Signal::Quit) {
101                        return;
102                    }
103                }
104            });
105    }
106}
107
108#[cfg(target_family = "windows")]
109/// Register signal handler.
110///
111/// Signals are handled by oneshots, you have to re-register
112/// after each signal.
113pub(crate) fn start<T: Send + 'static>(srv: Server<T>) {
114    if !register_system(srv) {
115        let _ = thread::Builder::new()
116            .name("ntex-server signals".to_string())
117            .spawn(move || {
118                ctrlc::set_handler(|| handle_signal(Signal::Int))
119                    .expect("Error setting Ctrl-C handler");
120            });
121    }
122}