diff options
| author | Do Nhat Minh <mrordinaire@gmail.com> | 2013-09-19 12:03:50 +0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2013-10-24 14:22:35 -0700 |
| commit | b5a02e07845b9fb4bc9b09909bd996c874fa3eed (patch) | |
| tree | 92d0986b83e14ca4b637cb933d11e46b272c5fc3 /src/libstd/rt/io | |
| parent | 816e46dd633cf4cc5741dde6ce3bffd4a9ba67a7 (diff) | |
| download | rust-b5a02e07845b9fb4bc9b09909bd996c874fa3eed.tar.gz rust-b5a02e07845b9fb4bc9b09909bd996c874fa3eed.zip | |
wrapping libuv signal for use in Rust
descriptive names easier-to-use api reorganize and document
Diffstat (limited to 'src/libstd/rt/io')
| -rw-r--r-- | src/libstd/rt/io/mod.rs | 3 | ||||
| -rw-r--r-- | src/libstd/rt/io/signal.rs | 190 |
2 files changed, 193 insertions, 0 deletions
diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 7f5b01bb1ec..758c9779165 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -326,6 +326,9 @@ pub mod native { /// Mock implementations for testing mod mock; +/// Signal handling +pub mod signal; + /// The default buffer size for various I/O operations static DEFAULT_BUF_SIZE: uint = 1024 * 64; diff --git a/src/libstd/rt/io/signal.rs b/src/libstd/rt/io/signal.rs new file mode 100644 index 00000000000..d3c260d361c --- /dev/null +++ b/src/libstd/rt/io/signal.rs @@ -0,0 +1,190 @@ +// 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. + +use comm::{Port, SharedChan, stream}; +use hashmap; +use option::{Some, None}; +use result::{Err, Ok}; +use rt::io::io_error; +use rt::local::Local; +use rt::rtio::{EventLoop, RtioSignalObject}; +use rt::sched::Scheduler; + +#[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 + /// 10 seconds to perfom any cleanup. After that, Windows will + /// 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. +/// +/// Example usage: +/// +/// ```rust +/// use std::rt::io::signal; +/// use std::task; +/// +/// let mut listener = signal::Listener(); +/// listener.register(signal::Interrupt); +/// +/// do task::spawn { +/// loop { +/// match listener.recv() { +/// signal::Interrupt => println("Got Interrupt'ed"), +/// _ => (), +/// } +/// } +/// } +/// +/// ``` +pub struct Listener { + /// A map from signums to handles to keep the handles in memory + priv handles: hashmap::HashMap<Signum, ~RtioSignalObject>, + /// chan is where all the handles send signums, which are received by + /// the clients from port. + priv chan: SharedChan<Signum>, + /// Clients of Listener can `recv()` from this port + port: Port<Signum>, +} + +impl Listener { + pub fn new() -> Listener { + let (port, chan) = stream(); + Listener { + chan: SharedChan::new(chan), + port: port, + handles: hashmap::HashMap::new(), + } + } + + /// Listen for a signal, returning true when successfully registered for + /// signum. Signals can be received using `recv()`. + pub fn register(&mut self, signum: Signum) -> bool { + match self.handles.find(&signum) { + Some(_) => true, // self is already listening to signum, so succeed + None => { + let chan = self.chan.clone(); + let handle = unsafe { + rtdebug!("Listener::register: borrowing io to init UvSignal"); + let sched: *mut Scheduler = Local::unsafe_borrow(); + rtdebug!("about to init handle"); + (*sched).event_loop.signal(signum, chan) + }; + match handle { + Ok(w) => { + self.handles.insert(signum, w); + true + }, + Err(ioerr) => { + rtdebug!("Listener::register: failed to init: {:?}", ioerr); + io_error::cond.raise(ioerr); + false + }, + } + }, + } + } + + /// Unregister a signal. + pub fn unregister(&mut self, signum: Signum) { + self.handles.pop(&signum); + } +} + +#[cfg(test)] +mod test { + use libc; + use rt::io::timer; + use super::*; + + // kill is only available on Unixes + #[cfg(unix)] + #[fixed_stack_segment] + fn sigint() { + unsafe { + libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT); + } + } + + #[test] + fn test_io_signal_smoketest() { + let mut signal = Listener::new(); + signal.register(Interrupt); + sigint(); + timer::sleep(10); + match signal.port.recv() { + Interrupt => (), + s => fail2!("Expected Interrupt, got {:?}", s), + } + } + + #[test] + fn test_io_signal_two_signal_one_signum() { + let mut s1 = Listener::new(); + let mut s2 = Listener::new(); + s1.register(Interrupt); + s2.register(Interrupt); + sigint(); + timer::sleep(10); + match s1.port.recv() { + Interrupt => (), + s => fail2!("Expected Interrupt, got {:?}", s), + } + match s1.port.recv() { + Interrupt => (), + s => fail2!("Expected Interrupt, got {:?}", s), + } + } + + #[test] + fn test_io_signal_unregister() { + let mut s1 = Listener::new(); + let mut s2 = Listener::new(); + s1.register(Interrupt); + s2.register(Interrupt); + s2.unregister(Interrupt); + sigint(); + timer::sleep(10); + if s2.port.peek() { + fail2!("Unexpected {:?}", s2.port.recv()); + } + } + + #[cfg(windows)] + #[test] + fn test_io_signal_invalid_signum() { + let mut s = Listener::new(); + if s.register(User1) { + fail2!("Unexpected successful registry of signum {:?}", User1); + } + } +} |
