about summary refs log tree commit diff
path: root/src/libstd/rt/io
diff options
context:
space:
mode:
authorDo Nhat Minh <mrordinaire@gmail.com>2013-09-19 12:03:50 +0800
committerAlex Crichton <alex@alexcrichton.com>2013-10-24 14:22:35 -0700
commitb5a02e07845b9fb4bc9b09909bd996c874fa3eed (patch)
tree92d0986b83e14ca4b637cb933d11e46b272c5fc3 /src/libstd/rt/io
parent816e46dd633cf4cc5741dde6ce3bffd4a9ba67a7 (diff)
downloadrust-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.rs3
-rw-r--r--src/libstd/rt/io/signal.rs190
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);
+        }
+    }
+}