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