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 collections::MutableSeq;
24 use comm::{Sender, Receiver, channel};
29 use option::{Some, None};
31 use result::{Ok, Err};
32 use rt::rtio::{IoFactory, LocalIo, RtioSignal, Callback};
33 use slice::ImmutableVector;
36 /// Signals that can be sent and received
38 #[deriving(PartialEq, Hash, Show)]
40 /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
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.
47 /// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
49 /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
51 /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
52 StopTemporarily = 20i,
53 /// Equivalent to SIGUSR1.
55 /// Equivalent to SIGUSR2.
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,
63 /// Listener provides a receiver to listen for registered signals.
65 /// Listener automatically unregisters its handles once it is out of scope.
66 /// However, clients can still unregister signums manually.
71 /// # #![allow(unused_must_use)]
72 /// use std::io::signal::{Listener, Interrupt};
74 /// let mut listener = Listener::new();
75 /// listener.register(Interrupt);
78 /// match listener.rx.recv() {
79 /// Interrupt => println!("Got Interrupt'ed"),
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.
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
94 pub rx: Receiver<Signum>,
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();
109 /// Listen for a signal, returning true when successfully registered for
110 /// signum. Signals can be received using `recv()`.
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).
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.
123 /// If this function fails to register a signal handler, then an error will
125 pub fn register(&mut self, signum: Signum) -> io::IoResult<()> {
126 struct SignalCallback {
130 impl Callback for SignalCallback {
131 fn call(&mut self) { self.tx.send(self.signum) }
134 if self.handles.iter().any(|&(sig, _)| sig == signum) {
135 return Ok(()); // self is already listening to signum, so succeed
137 match LocalIo::maybe_raise(|io| {
138 io.signal(signum as int, box SignalCallback {
144 self.handles.push((signum, handle));
147 Err(e) => Err(io::IoError::from_rtio_error(e))
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)),
169 use super::{Listener, Interrupt};
173 libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
177 #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
178 fn test_io_signal_smoketest() {
179 let mut signal = Listener::new();
180 signal.register(Interrupt).unwrap();
183 match signal.rx.recv() {
185 s => fail!("Expected Interrupt, got {:?}", s),
189 #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
190 fn test_io_signal_two_signal_one_signum() {
191 let mut s1 = Listener::new();
192 let mut s2 = Listener::new();
193 s1.register(Interrupt).unwrap();
194 s2.register(Interrupt).unwrap();
199 s => fail!("Expected Interrupt, got {:?}", s),
203 s => fail!("Expected Interrupt, got {:?}", s),
207 #[test] #[cfg(not(target_os="android"))] // FIXME(#10378)
208 fn test_io_signal_unregister() {
209 let mut s1 = Listener::new();
210 let mut s2 = Listener::new();
211 s1.register(Interrupt).unwrap();
212 s2.register(Interrupt).unwrap();
213 s2.unregister(Interrupt);
216 assert_eq!(s2.rx.try_recv(), Err(Empty));
220 #[cfg(test, windows)]
222 use super::{User1, Listener};
223 use result::{Ok, Err};
226 fn test_io_signal_invalid_signum() {
227 let mut s = Listener::new();
228 match s.register(User1) {
230 fail!("Unexpected successful registry of signum {:?}", User1);