(index<- ) ./libstd/io/signal.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
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 owned::Box;
30 use result::{Ok, Err};
31 use rt::rtio::{IoFactory, LocalIo, RtioSignal};
32 use slice::ImmutableVector;
33 use vec::Vec;
34
35 /// Signals that can be sent and received
36 #[repr(int)]
37 #[deriving(Eq, 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 if self.handles.iter().any(|&(sig, _)| sig == signum) {
126 return Ok(()); // self is already listening to signum, so succeed
127 }
128 match LocalIo::maybe_raise(|io| {
129 io.signal(signum, self.tx.clone())
130 }) {
131 Ok(handle) => {
132 self.handles.push((signum, handle));
133 Ok(())
134 }
135 Err(e) => Err(e)
136 }
137 }
138
139 /// Unregisters a signal. If this listener currently had a handler
140 /// registered for the signal, then it will stop receiving any more
141 /// notification about the signal. If the signal has already been received,
142 /// it may still be returned by `recv`.
143 pub fn unregister(&mut self, signum: Signum) {
144 match self.handles.iter().position(|&(i, _)| i == signum) {
145 Some(i) => drop(self.handles.remove(i)),
146 None => {}
147 }
148 }
149 }
150
151 #[cfg(test, unix)]
152 mod test_unix {
153 use prelude::*;
154 use libc;
155 use comm::Empty;
156 use io::timer;
157 use super::{Listener, Interrupt};
158
159 fn sigint() {
160 unsafe {
161 libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
162 }
163 }
164
165 #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
166 fn test_io_signal_smoketest() {
167 let mut signal = Listener::new();
168 signal.register(Interrupt).unwrap();
169 sigint();
170 timer::sleep(10);
171 match signal.rx.recv() {
172 Interrupt => (),
173 s => fail!("Expected Interrupt, got {:?}", s),
174 }
175 }
176
177 #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
178 fn test_io_signal_two_signal_one_signum() {
179 let mut s1 = Listener::new();
180 let mut s2 = Listener::new();
181 s1.register(Interrupt).unwrap();
182 s2.register(Interrupt).unwrap();
183 sigint();
184 timer::sleep(10);
185 match s1.rx.recv() {
186 Interrupt => (),
187 s => fail!("Expected Interrupt, got {:?}", s),
188 }
189 match s2.rx.recv() {
190 Interrupt => (),
191 s => fail!("Expected Interrupt, got {:?}", s),
192 }
193 }
194
195 #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
196 fn test_io_signal_unregister() {
197 let mut s1 = Listener::new();
198 let mut s2 = Listener::new();
199 s1.register(Interrupt).unwrap();
200 s2.register(Interrupt).unwrap();
201 s2.unregister(Interrupt);
202 sigint();
203 timer::sleep(10);
204 assert_eq!(s2.rx.try_recv(), Err(Empty));
205 }
206 }
207
208 #[cfg(test, windows)]
209 mod test_windows {
210 use super::{User1, Listener};
211 use result::{Ok, Err};
212
213 #[test]
214 fn test_io_signal_invalid_signum() {
215 let mut s = Listener::new();
216 match s.register(User1) {
217 Ok(..) => {
218 fail!("Unexpected successful registry of signum {:?}", User1);
219 }
220 Err(..) => {}
221 }
222 }
223 }
libstd/io/signal.rs:37:28-37:28 -enum- definition:
pub enum Signum {
/// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
Break = 21i,
references:- 1284: /// 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
--
142: /// it may still be returned by `recv`.
143: pub fn unregister(&mut self, signum: Signum) {
144: match self.handles.iter().position(|&(i, _)| i == signum) {
libstd/rt/rtio.rs:
198: -> IoResult<Box<RtioTTY:Send>>;
199: fn signal(&mut self, signal: Signum, channel: Sender<Signum>)
200: -> IoResult<Box<RtioSignal:Send>>;
libstd/io/signal.rs:
87: /// the clients from the receiver.
88: tx: Sender<Signum>,
libstd/io/signal.rs:82:8-82:8 -struct- definition:
/// ```
pub struct Listener {
/// A map from signums to handles to keep the handles in memory
references:- 396: 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,