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