alsh_libuv/handles/
signal.rs

1use crate::{FromInner, HandleTrait, Inner, IntoInner};
2use std::convert::TryFrom;
3use uv::{uv_signal_init, uv_signal_start, uv_signal_start_oneshot, uv_signal_stop, uv_signal_t};
4
5callbacks! {
6    pub SignalCB(handle: SignalHandle, signum: i32);
7}
8
9/// Additional data stored on the handle
10#[derive(Default)]
11pub(crate) struct SignalDataFields<'a> {
12    signal_cb: SignalCB<'a>,
13}
14
15/// Callback for uv_signal_start
16extern "C" fn uv_signal_cb(handle: *mut uv_signal_t, signum: std::os::raw::c_int) {
17    let dataptr = crate::Handle::get_data(uv_handle!(handle));
18    if !dataptr.is_null() {
19        unsafe {
20            if let super::SignalData(d) = &mut (*dataptr).addl {
21                d.signal_cb.call(handle.into_inner(), signum as _);
22            }
23        }
24    }
25}
26
27/// Signal handles implement Unix style signal handling on a per-event loop bases.
28///
29/// Windows notes: Reception of some signals is emulated:
30///   * SIGINT is normally delivered when the user presses CTRL+C. However, like on Unix, it is not
31///     generated when terminal raw mode is enabled.
32///   * SIGBREAK is delivered when the user pressed CTRL + BREAK.
33///   * SIGHUP is generated when the user closes the console window. On SIGHUP the program is given
34///     approximately 10 seconds to perform cleanup. After that Windows will unconditionally
35///     terminate it.
36///   * SIGWINCH is raised whenever libuv detects that the console has been resized. When a libuv
37///     app is running under a console emulator, or when a 32-bit libuv app is running on 64-bit
38///     system, SIGWINCH will be emulated. In such cases SIGWINCH signals may not always be
39///     delivered in a timely manner. For a writable TtyHandle libuv will only detect size changes
40///     when the cursor is moved. When a readable TtyHandle is used, resizing of the console buffer
41///     will be detected only if the handle is in raw mode and is being read.
42///   * Watchers for other signals can be successfully created, but these signals are never
43///     received. These signals are: SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGTERM and SIGKILL.
44///   * Calls to raise() or abort() to programmatically raise a signal are not detected by libuv;
45///     these will not trigger a signal watcher.
46///
47/// Unix notes
48///   * SIGKILL and SIGSTOP are impossible to catch.
49///   * Handling SIGBUS, SIGFPE, SIGILL or SIGSEGV via libuv results into undefined behavior.
50///   * SIGABRT will not be caught by libuv if generated by abort(), e.g. through assert().
51///   * On Linux SIGRT0 and SIGRT1 (signals 32 and 33) are used by the NPTL pthreads library to
52///     manage threads. Installing watchers for those signals will lead to unpredictable behavior
53///     and is strongly discouraged. Future versions of libuv may simply reject them.
54#[derive(Clone, Copy)]
55pub struct SignalHandle {
56    handle: *mut uv_signal_t,
57}
58
59impl SignalHandle {
60    /// Create and initialize a new signal handle
61    pub fn new(r#loop: &crate::Loop) -> crate::Result<SignalHandle> {
62        let layout = std::alloc::Layout::new::<uv_signal_t>();
63        let handle = unsafe { std::alloc::alloc(layout) as *mut uv_signal_t };
64        if handle.is_null() {
65            return Err(crate::Error::ENOMEM);
66        }
67
68        let ret = unsafe { uv_signal_init(r#loop.into_inner(), handle) };
69        if ret < 0 {
70            unsafe { std::alloc::dealloc(handle as _, layout) };
71            return Err(crate::Error::from_inner(ret as uv::uv_errno_t));
72        }
73
74        crate::Handle::initialize_data(uv_handle!(handle), super::SignalData(Default::default()));
75
76        Ok(SignalHandle { handle })
77    }
78
79    /// Start the handle with the given callback, watching for the given signal.
80    pub fn start<CB: Into<SignalCB<'static>>>(&mut self, cb: CB, signum: i32) -> crate::Result<()> {
81        // uv_cb is either Some(uv_signal_cb) or None
82        let cb = cb.into();
83        let uv_cb = use_c_callback!(uv_signal_cb, cb);
84
85        // cb is either Some(closure) or None - it is saved into data
86        let dataptr = crate::Handle::get_data(uv_handle!(self.handle));
87        if !dataptr.is_null() {
88            if let super::SignalData(d) = unsafe { &mut (*dataptr).addl } {
89                d.signal_cb = cb;
90            }
91        }
92
93        crate::uvret(unsafe { uv_signal_start(self.handle, uv_cb, signum as _) })
94    }
95
96    /// Same functionality as start() but the signal handler is reset the moment the signal is
97    /// received.
98    pub fn start_oneshot<CB: Into<SignalCB<'static>>>(
99        &mut self,
100        cb: CB,
101        signum: i32,
102    ) -> crate::Result<()> {
103        // uv_cb is either Some(uv_signal_cb) or None
104        let cb = cb.into();
105        let uv_cb = use_c_callback!(uv_signal_cb, cb);
106
107        // cb is either Some(closure) or None - it is saved into data
108        let dataptr = crate::Handle::get_data(uv_handle!(self.handle));
109        if !dataptr.is_null() {
110            if let super::SignalData(d) = unsafe { &mut (*dataptr).addl } {
111                d.signal_cb = cb;
112            }
113        }
114
115        crate::uvret(unsafe { uv_signal_start_oneshot(self.handle, uv_cb, signum as _) })
116    }
117
118    /// Stop the handle, the callback will no longer be called.
119    pub fn stop(&mut self) -> crate::Result<()> {
120        crate::uvret(unsafe { uv_signal_stop(self.handle) })
121    }
122
123    /// Signal being monitored by this handle.
124    pub fn signum(&self) -> i32 {
125        unsafe { (*self.handle).signum }
126    }
127}
128
129impl FromInner<*mut uv_signal_t> for SignalHandle {
130    fn from_inner(handle: *mut uv_signal_t) -> SignalHandle {
131        SignalHandle { handle }
132    }
133}
134
135impl Inner<*mut uv::uv_handle_t> for SignalHandle {
136    fn inner(&self) -> *mut uv::uv_handle_t {
137        uv_handle!(self.handle)
138    }
139}
140
141impl From<SignalHandle> for crate::Handle {
142    fn from(signal: SignalHandle) -> crate::Handle {
143        crate::Handle::from_inner(Inner::<*mut uv::uv_handle_t>::inner(&signal))
144    }
145}
146
147impl crate::ToHandle for SignalHandle {
148    fn to_handle(&self) -> crate::Handle {
149        crate::Handle::from_inner(Inner::<*mut uv::uv_handle_t>::inner(self))
150    }
151}
152
153impl TryFrom<crate::Handle> for SignalHandle {
154    type Error = crate::ConversionError;
155
156    fn try_from(handle: crate::Handle) -> Result<Self, Self::Error> {
157        let t = handle.get_type();
158        if t != crate::HandleType::SIGNAL {
159            Err(crate::ConversionError::new(t, crate::HandleType::SIGNAL))
160        } else {
161            Ok((handle.inner() as *mut uv_signal_t).into_inner())
162        }
163    }
164}
165
166impl HandleTrait for SignalHandle {}
167
168impl crate::Loop {
169    /// Create and initialize a new signal handle
170    pub fn signal(&self) -> crate::Result<SignalHandle> {
171        SignalHandle::new(self)
172    }
173}