]> git.lizzy.rs Git - rust.git/blob - src/libstd/io/signal.rs
d46f437cddd50a500badf5021657b7528866b4b7
[rust.git] / src / libstd / io / signal.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*!
12
13 Signal handling
14
15 This modules provides bindings to receive signals safely, built on top of the
16 local I/O factory. There are a number of defined signals which can be caught,
17 but not all signals will work across all platforms (windows doesn't have
18 definitions for a number of signals.
19
20 */
21
22 use clone::Clone;
23 use comm::{Sender, Receiver, channel};
24 use io;
25 use iter::Iterator;
26 use kinds::Send;
27 use mem::drop;
28 use option::{Some, None};
29 use boxed::Box;
30 use result::{Ok, Err};
31 use rt::rtio::{IoFactory, LocalIo, RtioSignal, Callback};
32 use slice::ImmutableVector;
33 use vec::Vec;
34
35 /// Signals that can be sent and received
36 #[repr(int)]
37 #[deriving(PartialEq, Hash, Show)]
38 pub enum Signum {
39     /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
40     Break = 21i,
41     /// Equivalent to SIGHUP, delivered when the user closes the terminal
42     /// window. On delivery of HangUp, the program is given approximately
43     /// 10 seconds to perform any cleanup. After that, Windows will
44     /// unconditionally terminate it.
45     HangUp = 1i,
46     /// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
47     Interrupt = 2i,
48     /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
49     Quit = 3i,
50     /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
51     StopTemporarily = 20i,
52     /// Equivalent to SIGUSR1.
53     User1 = 10i,
54     /// Equivalent to SIGUSR2.
55     User2 = 12i,
56     /// Equivalent to SIGWINCH, delivered when the console has been resized.
57     /// WindowSizeChange may not be delivered in a timely manner; size change
58     /// will only be detected when the cursor is being moved.
59     WindowSizeChange = 28i,
60 }
61
62 /// Listener provides a receiver to listen for registered signals.
63 ///
64 /// Listener automatically unregisters its handles once it is out of scope.
65 /// However, clients can still unregister signums manually.
66 ///
67 /// # Example
68 ///
69 /// ```rust,no_run
70 /// # #![allow(unused_must_use)]
71 /// use std::io::signal::{Listener, Interrupt};
72 ///
73 /// let mut listener = Listener::new();
74 /// listener.register(Interrupt);
75 ///
76 /// loop {
77 ///     match listener.rx.recv() {
78 ///         Interrupt => println!("Got Interrupt'ed"),
79 ///         _ => (),
80 ///     }
81 /// }
82 /// ```
83 pub struct Listener {
84     /// A map from signums to handles to keep the handles in memory
85     handles: Vec<(Signum, Box<RtioSignal + Send>)>,
86     /// This is where all the handles send signums, which are received by
87     /// the clients from the receiver.
88     tx: Sender<Signum>,
89
90     /// Clients of Listener can `recv()` on this receiver. This is exposed to
91     /// allow selection over it as well as manipulation of the receiver
92     /// directly.
93     pub rx: Receiver<Signum>,
94 }
95
96 impl Listener {
97     /// Creates a new listener for signals. Once created, signals are bound via
98     /// the `register` method (otherwise nothing will ever be received)
99     pub fn new() -> Listener {
100         let (tx, rx) = channel();
101         Listener {
102             tx: tx,
103             rx: rx,
104             handles: vec!(),
105         }
106     }
107
108     /// Listen for a signal, returning true when successfully registered for
109     /// signum. Signals can be received using `recv()`.
110     ///
111     /// Once a signal is registered, this listener will continue to receive
112     /// notifications of signals until it is unregistered. This occurs
113     /// regardless of the number of other listeners registered in other tasks
114     /// (or on this task).
115     ///
116     /// Signals are still received if there is no task actively waiting for
117     /// a signal, and a later call to `recv` will return the signal that was
118     /// received while no task was waiting on it.
119     ///
120     /// # Error
121     ///
122     /// If this function fails to register a signal handler, then an error will
123     /// be returned.
124     pub fn register(&mut self, signum: Signum) -> io::IoResult<()> {
125         struct SignalCallback {
126             signum: Signum,
127             tx: Sender<Signum>,
128         }
129         impl Callback for SignalCallback {
130             fn call(&mut self) { self.tx.send(self.signum) }
131         }
132
133         if self.handles.iter().any(|&(sig, _)| sig == signum) {
134             return Ok(()); // self is already listening to signum, so succeed
135         }
136         match LocalIo::maybe_raise(|io| {
137             io.signal(signum as int, box SignalCallback {
138                 signum: signum,
139                 tx: self.tx.clone(),
140             })
141         }) {
142             Ok(handle) => {
143                 self.handles.push((signum, handle));
144                 Ok(())
145             }
146             Err(e) => Err(io::IoError::from_rtio_error(e))
147         }
148     }
149
150     /// Unregisters a signal. If this listener currently had a handler
151     /// registered for the signal, then it will stop receiving any more
152     /// notification about the signal. If the signal has already been received,
153     /// it may still be returned by `recv`.
154     pub fn unregister(&mut self, signum: Signum) {
155         match self.handles.iter().position(|&(i, _)| i == signum) {
156             Some(i) => drop(self.handles.remove(i)),
157             None => {}
158         }
159     }
160 }
161
162 #[cfg(test, unix)]
163 mod test_unix {
164     use prelude::*;
165     use libc;
166     use comm::Empty;
167     use io::timer;
168     use super::{Listener, Interrupt};
169
170     fn sigint() {
171         unsafe {
172             libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
173         }
174     }
175
176     #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
177     fn test_io_signal_smoketest() {
178         let mut signal = Listener::new();
179         signal.register(Interrupt).unwrap();
180         sigint();
181         timer::sleep(10);
182         match signal.rx.recv() {
183             Interrupt => (),
184             s => fail!("Expected Interrupt, got {:?}", s),
185         }
186     }
187
188     #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
189     fn test_io_signal_two_signal_one_signum() {
190         let mut s1 = Listener::new();
191         let mut s2 = Listener::new();
192         s1.register(Interrupt).unwrap();
193         s2.register(Interrupt).unwrap();
194         sigint();
195         timer::sleep(10);
196         match s1.rx.recv() {
197             Interrupt => (),
198             s => fail!("Expected Interrupt, got {:?}", s),
199         }
200         match s2.rx.recv() {
201             Interrupt => (),
202             s => fail!("Expected Interrupt, got {:?}", s),
203         }
204     }
205
206     #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
207     fn test_io_signal_unregister() {
208         let mut s1 = Listener::new();
209         let mut s2 = Listener::new();
210         s1.register(Interrupt).unwrap();
211         s2.register(Interrupt).unwrap();
212         s2.unregister(Interrupt);
213         sigint();
214         timer::sleep(10);
215         assert_eq!(s2.rx.try_recv(), Err(Empty));
216     }
217 }
218
219 #[cfg(test, windows)]
220 mod test_windows {
221     use super::{User1, Listener};
222     use result::{Ok, Err};
223
224     #[test]
225     fn test_io_signal_invalid_signum() {
226         let mut s = Listener::new();
227         match s.register(User1) {
228             Ok(..) => {
229                 fail!("Unexpected successful registry of signum {:?}", User1);
230             }
231             Err(..) => {}
232         }
233     }
234 }