asyncio/
signal_set.rs

1use unsafe_cell::UnsafeRefCell;
2use ffi::{RawFd, AsRawFd, getnonblock, setnonblock};
3use error::{ErrCode, READY, ECANCELED, EINTR, EAGAIN, last_error, eof};
4use core::{IoContext, AsIoContext, ThreadIoContext, AsyncFd, workplace};
5use async::{Receiver, Handler, WrappedHandler, Operation};
6use reactive_io::{AsAsyncFd, AsyncInput, cancel};
7
8use std::io;
9use std::mem;
10use std::ptr;
11use libc::{self, SFD_CLOEXEC, SIG_SETMASK, c_void, ssize_t, sigset_t, signalfd_siginfo,
12           signalfd, sigemptyset, sigaddset, sigdelset, pthread_sigmask};
13
14/// A list specifying POSIX categories of signal.
15#[repr(i32)]
16#[derive(Clone, Eq, PartialEq, Debug)]
17pub enum Signal {
18    /// Hangup detected on controlling terminal or death of controlling process.
19    SIGHUP = libc::SIGHUP,
20
21    /// Interrupt from keyboard.
22    SIGINT = libc::SIGINT,
23
24    /// Quit from keyboard.
25    SIGQUIT = libc::SIGQUIT,
26
27    /// Illegal Instruction.
28    SIGILL = libc::SIGILL,
29
30    /// Abort signal from abort(3)
31    SIGABRT = libc::SIGABRT,
32
33    /// Floating point exception.
34    SIGFPE = libc::SIGFPE,
35
36    /// Kill signal.
37    SIGKILL = libc::SIGKILL,
38
39    /// Invalid memory reference.
40    SIGSEGV = libc::SIGSEGV,
41
42    /// Broken pipe: write to pipe with no readers.
43    SIGPIPE = libc::SIGPIPE,
44
45    /// Timer signal from alarm(2).
46    SIGALRM = libc::SIGALRM,
47
48    /// Termination signal.
49    SIGTERM = libc::SIGTERM,
50
51    /// User-defined signal 1.
52    SIGUSR1 = libc::SIGUSR1,
53
54    /// User-defined signal 2.
55    SIGUSR2 = libc::SIGUSR2,
56
57    /// Child stopped of terminated.
58    SIGCHLD = libc::SIGCHLD,
59
60    /// Continue if stopped.
61    SIGCONT = libc::SIGCONT,
62
63    /// Stop process.
64    SIGSTOP = libc::SIGSTOP,
65
66    /// Stop typed at terminal.
67    SIGTSTP = libc::SIGTSTP,
68
69    /// Terminal input for background process.
70    SIGTTIN = libc::SIGTTIN,
71
72    /// Terminal output for background process.
73    SIGTTOU = libc::SIGTTOU,
74
75    /// Bus error (bad memory access).
76    SIGBUS = libc::SIGBUS,
77
78    /// Pollable event (Sys V). Synonym for SIGIO.
79    SIGPOLL = libc::SIGPOLL,
80
81    /// Profiling timer expired.
82    SIGPROF = libc::SIGPROF,
83
84    /// Bad argument to routine (SVr4).
85    SIGSYS = libc::SIGSYS,
86
87    /// Trace/breakpoint trap.
88    SIGTRAP = libc::SIGTRAP,
89
90    /// Urgent condition on socket (4.2BSD).
91    SIGURG = libc::SIGURG,
92
93    /// Virtual alarm clock (4.2BSD).
94    SIGVTALRM = libc::SIGVTALRM,
95
96    /// CPU time limit exceeded (4.2BSD).
97    SIGXCPU = libc::SIGXCPU,
98
99    /// File size limit exceeded (4.2BSD).
100    SIGXFSZ = libc::SIGXFSZ,
101}
102
103pub fn raise(signal: Signal) -> io::Result<()> {
104    libc_try!(libc::raise(signal as i32));
105    Ok(())
106}
107
108fn signalfd_init() -> io::Result<(RawFd, sigset_t)> {
109    let mut mask: sigset_t = unsafe { mem::uninitialized() };
110    libc_ign!(sigemptyset(&mut mask));
111    let sfd = libc_try!(signalfd(-1, &mask, SFD_CLOEXEC));
112    Ok((sfd, mask))
113}
114
115fn signalfd_add(sfd: RawFd, mask: &mut sigset_t, signal: Signal) -> io::Result<()> {
116    libc_try!(sigaddset(mask, signal as i32));
117    libc_ign!(pthread_sigmask(SIG_SETMASK, mask, ptr::null_mut()));
118    libc_ign!(signalfd(sfd, mask, 0));
119    Ok(())
120}
121
122fn signalfd_del(sfd: RawFd, mask: &mut sigset_t, signal: Signal) -> io::Result<()> {
123    libc_try!(sigdelset(mask, signal as i32));
124    libc_ign!(pthread_sigmask(SIG_SETMASK, mask, ptr::null_mut()));
125    libc_ign!(signalfd(sfd, mask, 0));
126    Ok(())
127}
128
129fn signalfd_reset(sfd: RawFd, mask: &mut sigset_t) -> io::Result<()> {
130    libc_try!(sigemptyset(mask));
131    libc_ign!(pthread_sigmask(SIG_SETMASK, mask, ptr::null_mut()));
132    libc_ign!(signalfd(sfd, mask, 0));
133    Ok(())
134}
135
136unsafe fn signalfd_read(sfd: RawFd, ssi: &mut signalfd_siginfo) -> ssize_t
137{
138    libc::read(sfd, ssi as *mut _ as *mut c_void, mem::size_of_val(ssi))
139}
140
141fn signalfd_wait<T>(sfd: &T) -> io::Result<Signal>
142    where T: AsyncInput,
143{
144    while !sfd.as_ctx().stopped() {
145        let mut ssi: signalfd_siginfo = unsafe { mem::uninitialized() };
146        let len = unsafe { signalfd_read(sfd.as_raw_fd(), &mut ssi) };
147        if len > 0 {
148            return Ok(unsafe { mem::transmute(ssi.ssi_signo) });
149        }
150        if len == 0 {
151            return Err(eof());
152        }
153        let ec = last_error();
154        if ec != EINTR {
155            return Err(ec.into());
156        }
157    }
158    Err(ECANCELED.into())
159}
160
161struct SignalHandler<T> {
162    sfd: UnsafeRefCell<T>
163}
164
165impl<T> WrappedHandler<Signal, io::Error> for SignalHandler<T>
166    where T: AsyncInput,
167{
168    fn perform(&mut self, ctx: &IoContext, this: &mut ThreadIoContext, ec: ErrCode, op: Operation<Signal, io::Error, Self>) {
169        let sfd = unsafe { self.sfd.as_ref() };
170        match ec {
171            READY => {
172                let mode = getnonblock(sfd).unwrap();
173                setnonblock(sfd, true).unwrap();
174
175                while !ctx.stopped() {
176                    let mut ssi: signalfd_siginfo = unsafe { mem::uninitialized() };
177                    let len = unsafe { signalfd_read(sfd.as_raw_fd(), &mut ssi) };
178                    if len > 0 {
179                        setnonblock(sfd, mode).unwrap();
180                        sfd.next_op(this);
181                        op.send(ctx, Ok(unsafe { mem::transmute(ssi.ssi_signo) }));
182                        return;
183                    }
184                    if len == 0 {
185                        setnonblock(sfd, mode).unwrap();
186                        sfd.next_op(this);
187                        op.send(ctx, Err(eof()));
188                        return;
189                    }
190
191                    let ec = last_error();
192                    if ec == EAGAIN {
193                        setnonblock(sfd, mode).unwrap();
194                        sfd.add_op(this, op, ec);
195                        return;
196                    }
197                    if ec != EINTR {
198                        setnonblock(sfd, mode).unwrap();
199                        sfd.next_op(this);
200                        op.send(ctx, Err(ec.into()));
201                        return;
202                    }
203                }
204
205                setnonblock(sfd, mode).unwrap();
206                sfd.next_op(this);
207                op.send(ctx, Err(ECANCELED.into()));
208                return;
209            },
210            ec => op.send(ctx, Err(ec.into())),
211        }
212    }
213}
214
215fn signalfd_async_wait<T, F>(sfd: &T, handler: F) -> F::Output
216    where T: AsyncInput,
217          F: Handler<Signal, io::Error>,
218{
219    let (op, res) = handler.channel(SignalHandler { sfd: UnsafeRefCell::new(sfd) });
220    workplace(sfd.as_ctx(), |this| sfd.add_op(this, op, READY));
221    res.recv(sfd.as_ctx())
222}
223
224/// Provides a signal handing.
225pub struct SignalSet {
226    fd: AsyncFd,
227    mask: sigset_t,
228}
229
230impl Drop for SignalSet {
231    fn drop(&mut self) {
232        signalfd_reset(self.fd.as_raw_fd(), &mut self.mask).unwrap();
233    }
234}
235
236impl SignalSet {
237    pub fn new(ctx: &IoContext) -> io::Result<SignalSet> {
238        let (fd, mask) = try!(signalfd_init());
239        Ok(SignalSet {
240            fd: AsyncFd::new::<Self>(fd, ctx),
241            mask: mask,
242        })
243    }
244
245    pub fn add(&mut self, signal: Signal) -> io::Result<()> {
246        signalfd_add(self.as_raw_fd(), &mut self.mask, signal)
247    }
248
249    pub fn async_wait<F>(&self, handler: F) -> F::Output
250        where F: Handler<Signal, io::Error>,
251    {
252        signalfd_async_wait(self, handler)
253    }
254
255    pub fn cancel(&self) -> &Self {
256        cancel(self);
257        self
258    }
259
260    pub fn clear(&mut self) -> io::Result<()> {
261        signalfd_reset(self.as_raw_fd(), &mut self.mask)
262    }
263
264    pub fn remove(&mut self, signal: Signal) -> io::Result<()> {
265        signalfd_del(self.as_raw_fd(), &mut self.mask, signal)
266    }
267
268    pub fn wait(&self) -> io::Result<Signal> {
269        signalfd_wait(self)
270    }
271}
272
273impl AsRawFd for SignalSet {
274    fn as_raw_fd(&self) -> RawFd {
275        self.fd.as_raw_fd()
276    }
277}
278
279unsafe impl Send for SignalSet { }
280
281unsafe impl AsIoContext for SignalSet {
282    fn as_ctx(&self) -> &IoContext {
283        self.fd.as_ctx()
284    }
285}
286
287impl AsAsyncFd for SignalSet {
288    fn as_fd(&self) -> &AsyncFd {
289        &self.fd
290    }
291}
292
293#[test]
294fn test_signal_set() {
295    use core::IoContext;
296
297    let ctx = &IoContext::new().unwrap();
298    let mut sig = SignalSet::new(ctx).unwrap();
299    sig.add(Signal::SIGHUP).unwrap();
300    sig.add(Signal::SIGUSR1).unwrap();
301    sig.remove(Signal::SIGUSR1).unwrap();
302    sig.remove(Signal::SIGUSR2).unwrap();
303}
304
305#[test]
306fn test_signal_set_wait() {
307    use core::IoContext;
308
309    let ctx = &IoContext::new().unwrap();
310    let mut sig = SignalSet::new(ctx).unwrap();
311    sig.add(Signal::SIGHUP).unwrap();
312    sig.add(Signal::SIGUSR1).unwrap();
313    raise(Signal::SIGHUP).unwrap();
314    raise(Signal::SIGUSR1).unwrap();
315    assert_eq!(sig.wait().unwrap(), Signal::SIGHUP);
316    assert_eq!(sig.wait().unwrap(), Signal::SIGUSR1);
317}