2013-09-18 23:03:50 -05:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2013-10-22 10:51:44 -05:00
|
|
|
/*!
|
|
|
|
|
|
|
|
Signal handling
|
|
|
|
|
|
|
|
This modules provides bindings to receive signals safely, built on top of the
|
|
|
|
local I/O factory. There are a number of defined signals which can be caught,
|
|
|
|
but not all signals will work across all platforms (windows doesn't have
|
|
|
|
definitions for a number of signals.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2013-11-11 00:46:32 -06:00
|
|
|
use clone::Clone;
|
2014-01-29 18:33:57 -06:00
|
|
|
use result::{Ok, Err};
|
Rewrite channels yet again for upgradeability
This, the Nth rewrite of channels, is not a rewrite of the core logic behind
channels, but rather their API usage. In the past, we had the distinction
between oneshot, stream, and shared channels, but the most recent rewrite
dropped oneshots in favor of streams and shared channels.
This distinction of stream vs shared has shown that it's not quite what we'd
like either, and this moves the `std::comm` module in the direction of "one
channel to rule them all". There now remains only one Chan and one Port.
This new channel is actually a hybrid oneshot/stream/shared channel under the
hood in order to optimize for the use cases in question. Additionally, this also
reduces the cognitive burden of having to choose between a Chan or a SharedChan
in an API.
My simple benchmarks show no reduction in efficiency over the existing channels
today, and a 3x improvement in the oneshot case. I sadly don't have a
pre-last-rewrite compiler to test out the old old oneshots, but I would imagine
that the performance is comparable, but slightly slower (due to atomic reference
counting).
This commit also brings the bonus bugfix to channels that the pending queue of
messages are all dropped when a Port disappears rather then when both the Port
and the Chan disappear.
2014-01-08 20:31:48 -06:00
|
|
|
use comm::{Port, Chan};
|
2013-11-11 00:46:32 -06:00
|
|
|
use container::{Map, MutableMap};
|
2013-09-18 23:03:50 -05:00
|
|
|
use hashmap;
|
2014-01-29 18:33:57 -06:00
|
|
|
use io;
|
2013-12-05 19:25:48 -06:00
|
|
|
use rt::rtio::{IoFactory, LocalIo, RtioSignal};
|
2013-09-18 23:03:50 -05:00
|
|
|
|
2013-08-28 20:21:04 -05:00
|
|
|
#[repr(int)]
|
2013-09-18 23:03:50 -05:00
|
|
|
#[deriving(Eq, IterBytes)]
|
|
|
|
pub enum Signum {
|
|
|
|
/// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
|
|
|
|
Break = 21i,
|
|
|
|
/// Equivalent to SIGHUP, delivered when the user closes the terminal
|
|
|
|
/// window. On delivery of HangUp, the program is given approximately
|
2013-12-14 23:26:09 -06:00
|
|
|
/// 10 seconds to perform any cleanup. After that, Windows will
|
2013-09-18 23:03:50 -05:00
|
|
|
/// unconditionally terminate it.
|
|
|
|
HangUp = 1i,
|
|
|
|
/// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
|
|
|
|
Interrupt = 2i,
|
|
|
|
/// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
|
|
|
|
Quit = 3i,
|
|
|
|
/// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
|
|
|
|
StopTemporarily = 20i,
|
|
|
|
/// Equivalent to SIGUSR1.
|
|
|
|
User1 = 10i,
|
|
|
|
/// Equivalent to SIGUSR2.
|
|
|
|
User2 = 12i,
|
|
|
|
/// Equivalent to SIGWINCH, delivered when the console has been resized.
|
|
|
|
/// WindowSizeChange may not be delivered in a timely manner; size change
|
|
|
|
/// will only be detected when the cursor is being moved.
|
|
|
|
WindowSizeChange = 28i,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Listener provides a port to listen for registered signals.
|
|
|
|
///
|
|
|
|
/// Listener automatically unregisters its handles once it is out of scope.
|
|
|
|
/// However, clients can still unregister signums manually.
|
|
|
|
///
|
2013-10-22 10:51:44 -05:00
|
|
|
/// # Example
|
2013-09-18 23:03:50 -05:00
|
|
|
///
|
2013-12-22 15:31:23 -06:00
|
|
|
/// ```rust,ignore
|
2013-11-11 00:46:32 -06:00
|
|
|
/// use std::io::signal::{Listener, Interrupt};
|
2013-09-18 23:03:50 -05:00
|
|
|
///
|
2013-10-22 10:51:44 -05:00
|
|
|
/// let mut listener = Listener::new();
|
2013-12-22 15:31:23 -06:00
|
|
|
/// listener.register(Interrupt);
|
2013-09-18 23:03:50 -05:00
|
|
|
///
|
2014-01-26 21:42:26 -06:00
|
|
|
/// spawn({
|
2013-09-18 23:03:50 -05:00
|
|
|
/// loop {
|
2013-10-22 10:51:44 -05:00
|
|
|
/// match listener.port.recv() {
|
2014-01-09 04:06:55 -06:00
|
|
|
/// Interrupt => println!("Got Interrupt'ed"),
|
2013-09-18 23:03:50 -05:00
|
|
|
/// _ => (),
|
|
|
|
/// }
|
|
|
|
/// }
|
2014-01-26 21:42:26 -06:00
|
|
|
/// });
|
2013-09-18 23:03:50 -05:00
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
pub struct Listener {
|
|
|
|
/// A map from signums to handles to keep the handles in memory
|
2013-10-22 10:51:44 -05:00
|
|
|
priv handles: hashmap::HashMap<Signum, ~RtioSignal>,
|
2013-09-18 23:03:50 -05:00
|
|
|
/// chan is where all the handles send signums, which are received by
|
|
|
|
/// the clients from port.
|
Rewrite channels yet again for upgradeability
This, the Nth rewrite of channels, is not a rewrite of the core logic behind
channels, but rather their API usage. In the past, we had the distinction
between oneshot, stream, and shared channels, but the most recent rewrite
dropped oneshots in favor of streams and shared channels.
This distinction of stream vs shared has shown that it's not quite what we'd
like either, and this moves the `std::comm` module in the direction of "one
channel to rule them all". There now remains only one Chan and one Port.
This new channel is actually a hybrid oneshot/stream/shared channel under the
hood in order to optimize for the use cases in question. Additionally, this also
reduces the cognitive burden of having to choose between a Chan or a SharedChan
in an API.
My simple benchmarks show no reduction in efficiency over the existing channels
today, and a 3x improvement in the oneshot case. I sadly don't have a
pre-last-rewrite compiler to test out the old old oneshots, but I would imagine
that the performance is comparable, but slightly slower (due to atomic reference
counting).
This commit also brings the bonus bugfix to channels that the pending queue of
messages are all dropped when a Port disappears rather then when both the Port
and the Chan disappear.
2014-01-08 20:31:48 -06:00
|
|
|
priv chan: Chan<Signum>,
|
2013-10-22 10:51:44 -05:00
|
|
|
|
|
|
|
/// Clients of Listener can `recv()` from this port. This is exposed to
|
|
|
|
/// allow selection over this port as well as manipulation of the port
|
|
|
|
/// directly.
|
2013-09-18 23:03:50 -05:00
|
|
|
port: Port<Signum>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Listener {
|
2013-10-22 10:51:44 -05:00
|
|
|
/// Creates a new listener for signals. Once created, signals are bound via
|
|
|
|
/// the `register` method (otherwise nothing will ever be received)
|
2013-09-18 23:03:50 -05:00
|
|
|
pub fn new() -> Listener {
|
Rewrite channels yet again for upgradeability
This, the Nth rewrite of channels, is not a rewrite of the core logic behind
channels, but rather their API usage. In the past, we had the distinction
between oneshot, stream, and shared channels, but the most recent rewrite
dropped oneshots in favor of streams and shared channels.
This distinction of stream vs shared has shown that it's not quite what we'd
like either, and this moves the `std::comm` module in the direction of "one
channel to rule them all". There now remains only one Chan and one Port.
This new channel is actually a hybrid oneshot/stream/shared channel under the
hood in order to optimize for the use cases in question. Additionally, this also
reduces the cognitive burden of having to choose between a Chan or a SharedChan
in an API.
My simple benchmarks show no reduction in efficiency over the existing channels
today, and a 3x improvement in the oneshot case. I sadly don't have a
pre-last-rewrite compiler to test out the old old oneshots, but I would imagine
that the performance is comparable, but slightly slower (due to atomic reference
counting).
This commit also brings the bonus bugfix to channels that the pending queue of
messages are all dropped when a Port disappears rather then when both the Port
and the Chan disappear.
2014-01-08 20:31:48 -06:00
|
|
|
let (port, chan) = Chan::new();
|
2013-09-18 23:03:50 -05:00
|
|
|
Listener {
|
2013-12-05 20:19:06 -06:00
|
|
|
chan: chan,
|
2013-09-18 23:03:50 -05:00
|
|
|
port: port,
|
|
|
|
handles: hashmap::HashMap::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Listen for a signal, returning true when successfully registered for
|
|
|
|
/// signum. Signals can be received using `recv()`.
|
2013-10-22 10:51:44 -05:00
|
|
|
///
|
|
|
|
/// Once a signal is registered, this listener will continue to receive
|
|
|
|
/// notifications of signals until it is unregistered. This occurs
|
|
|
|
/// regardless of the number of other listeners registered in other tasks
|
|
|
|
/// (or on this task).
|
|
|
|
///
|
|
|
|
/// Signals are still received if there is no task actively waiting for
|
|
|
|
/// a signal, and a later call to `recv` will return the signal that was
|
|
|
|
/// received while no task was waiting on it.
|
|
|
|
///
|
2014-01-30 18:55:20 -06:00
|
|
|
/// # Error
|
2013-10-22 10:51:44 -05:00
|
|
|
///
|
|
|
|
/// If this function fails to register a signal handler, then an error will
|
2014-01-30 18:55:20 -06:00
|
|
|
/// be returned.
|
2014-01-29 18:33:57 -06:00
|
|
|
pub fn register(&mut self, signum: Signum) -> io::IoResult<()> {
|
2013-10-22 10:51:44 -05:00
|
|
|
if self.handles.contains_key(&signum) {
|
2014-01-29 18:33:57 -06:00
|
|
|
return Ok(()); // self is already listening to signum, so succeed
|
2013-09-18 23:03:50 -05:00
|
|
|
}
|
2013-12-12 19:30:41 -06:00
|
|
|
match LocalIo::maybe_raise(|io| {
|
|
|
|
io.signal(signum, self.chan.clone())
|
|
|
|
}) {
|
2014-01-29 18:33:57 -06:00
|
|
|
Ok(handle) => {
|
2013-12-12 19:30:41 -06:00
|
|
|
self.handles.insert(signum, handle);
|
2014-01-29 18:33:57 -06:00
|
|
|
Ok(())
|
2013-10-22 10:51:44 -05:00
|
|
|
}
|
2014-01-29 18:33:57 -06:00
|
|
|
Err(e) => Err(e)
|
2013-12-05 19:25:48 -06:00
|
|
|
}
|
2013-09-18 23:03:50 -05:00
|
|
|
}
|
|
|
|
|
2013-10-22 10:51:44 -05:00
|
|
|
/// Unregisters a signal. If this listener currently had a handler
|
|
|
|
/// registered for the signal, then it will stop receiving any more
|
|
|
|
/// notification about the signal. If the signal has already been received,
|
|
|
|
/// it may still be returned by `recv`.
|
2013-09-18 23:03:50 -05:00
|
|
|
pub fn unregister(&mut self, signum: Signum) {
|
|
|
|
self.handles.pop(&signum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use libc;
|
2013-12-22 00:15:04 -06:00
|
|
|
use comm::Empty;
|
2013-11-11 00:46:32 -06:00
|
|
|
use io::timer;
|
2013-10-25 19:04:37 -05:00
|
|
|
use super::{Listener, Interrupt};
|
2013-09-18 23:03:50 -05:00
|
|
|
|
|
|
|
// kill is only available on Unixes
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn sigint() {
|
|
|
|
unsafe {
|
|
|
|
libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-09 20:57:03 -06:00
|
|
|
#[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
|
2013-09-18 23:03:50 -05:00
|
|
|
fn test_io_signal_smoketest() {
|
|
|
|
let mut signal = Listener::new();
|
2014-01-30 16:10:53 -06:00
|
|
|
signal.register(Interrupt).unwrap();
|
2013-09-18 23:03:50 -05:00
|
|
|
sigint();
|
|
|
|
timer::sleep(10);
|
|
|
|
match signal.port.recv() {
|
|
|
|
Interrupt => (),
|
2013-10-22 11:36:30 -05:00
|
|
|
s => fail!("Expected Interrupt, got {:?}", s),
|
2013-09-18 23:03:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-09 20:57:03 -06:00
|
|
|
#[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
|
2013-09-18 23:03:50 -05:00
|
|
|
fn test_io_signal_two_signal_one_signum() {
|
|
|
|
let mut s1 = Listener::new();
|
|
|
|
let mut s2 = Listener::new();
|
2014-01-30 16:10:53 -06:00
|
|
|
s1.register(Interrupt).unwrap();
|
|
|
|
s2.register(Interrupt).unwrap();
|
2013-09-18 23:03:50 -05:00
|
|
|
sigint();
|
|
|
|
timer::sleep(10);
|
|
|
|
match s1.port.recv() {
|
|
|
|
Interrupt => (),
|
2013-10-22 11:36:30 -05:00
|
|
|
s => fail!("Expected Interrupt, got {:?}", s),
|
2013-09-18 23:03:50 -05:00
|
|
|
}
|
2013-10-27 01:31:14 -05:00
|
|
|
match s2.port.recv() {
|
2013-09-18 23:03:50 -05:00
|
|
|
Interrupt => (),
|
2013-10-22 11:36:30 -05:00
|
|
|
s => fail!("Expected Interrupt, got {:?}", s),
|
2013-09-18 23:03:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-09 20:57:03 -06:00
|
|
|
#[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378)
|
2013-09-18 23:03:50 -05:00
|
|
|
fn test_io_signal_unregister() {
|
|
|
|
let mut s1 = Listener::new();
|
|
|
|
let mut s2 = Listener::new();
|
2014-01-30 16:10:53 -06:00
|
|
|
s1.register(Interrupt).unwrap();
|
|
|
|
s2.register(Interrupt).unwrap();
|
2013-09-18 23:03:50 -05:00
|
|
|
s2.unregister(Interrupt);
|
|
|
|
sigint();
|
|
|
|
timer::sleep(10);
|
2013-12-22 00:15:04 -06:00
|
|
|
assert_eq!(s2.port.try_recv(), Empty);
|
2013-09-18 23:03:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
#[test]
|
|
|
|
fn test_io_signal_invalid_signum() {
|
2013-11-11 00:46:32 -06:00
|
|
|
use io;
|
2013-10-25 19:04:37 -05:00
|
|
|
use super::User1;
|
2014-02-01 13:24:42 -06:00
|
|
|
use result::{Ok, Err};
|
2013-09-18 23:03:50 -05:00
|
|
|
let mut s = Listener::new();
|
2013-10-22 11:36:30 -05:00
|
|
|
let mut called = false;
|
2014-01-30 18:55:20 -06:00
|
|
|
match s.register(User1) {
|
|
|
|
Ok(..) => {
|
2013-10-22 11:36:30 -05:00
|
|
|
fail!("Unexpected successful registry of signum {:?}", User1);
|
|
|
|
}
|
2014-01-30 18:55:20 -06:00
|
|
|
Err(..) => {}
|
|
|
|
}
|
2013-09-18 23:03:50 -05:00
|
|
|
}
|
|
|
|
}
|