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.
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.
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.
23 use comm::{Sender, Receiver, channel};
28 use option::{Some, None};
30 use result::{Ok, Err};
31 use rt::rtio::{IoFactory, LocalIo, RtioSignal, Callback};
32 use slice::ImmutableVector;
35 /// Signals that can be sent and received
37 #[deriving(PartialEq, Hash, Show)]
39 /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
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.
46 /// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
48 /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
50 /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
51 StopTemporarily = 20i,
52 /// Equivalent to SIGUSR1.
54 /// Equivalent to SIGUSR2.
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,
62 /// Listener provides a receiver to listen for registered signals.
64 /// Listener automatically unregisters its handles once it is out of scope.
65 /// However, clients can still unregister signums manually.
70 /// # #![allow(unused_must_use)]
71 /// use std::io::signal::{Listener, Interrupt};
73 /// let mut listener = Listener::new();
74 /// listener.register(Interrupt);
77 /// match listener.rx.recv() {
78 /// Interrupt => println!("Got Interrupt'ed"),
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.
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
93 pub rx: Receiver<Signum>,
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();
108 /// Listen for a signal, returning true when successfully registered for
109 /// signum. Signals can be received using `recv()`.
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).
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.
122 /// If this function fails to register a signal handler, then an error will
124 pub fn register(&mut self, signum: Signum) -> io::IoResult<()> {
125 struct SignalCallback {
129 impl Callback for SignalCallback {
130 fn call(&mut self) { self.tx.send(self.signum) }
133 if self.handles.iter().any(|&(sig, _)| sig == signum) {
134 return Ok(()); // self is already listening to signum, so succeed
136 match LocalIo::maybe_raise(|io| {
137 io.signal(signum as int, box SignalCallback {
143 self.handles.push((signum, handle));
146 Err(e) => Err(io::IoError::from_rtio_error(e))
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)),
168 use super::{Listener, Interrupt};
172 libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
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();
182 match signal.rx.recv() {
184 s => fail!("Expected Interrupt, got {:?}", s),
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();
198 s => fail!("Expected Interrupt, got {:?}", s),
202 s => fail!("Expected Interrupt, got {:?}", s),
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);
215 assert_eq!(s2.rx.try_recv(), Err(Empty));
219 #[cfg(test, windows)]
221 use super::{User1, Listener};
222 use result::{Ok, Err};
225 fn test_io_signal_invalid_signum() {
226 let mut s = Listener::new();
227 match s.register(User1) {
229 fail!("Unexpected successful registry of signum {:?}", User1);