]> git.lizzy.rs Git - rust.git/commitdiff
wrapping libuv signal for use in Rust
authorDo Nhat Minh <mrordinaire@gmail.com>
Thu, 19 Sep 2013 04:03:50 +0000 (12:03 +0800)
committerAlex Crichton <alex@alexcrichton.com>
Thu, 24 Oct 2013 21:22:35 +0000 (14:22 -0700)
descriptive names
easier-to-use api
reorganize and document

src/libstd/rt/io/mod.rs
src/libstd/rt/io/signal.rs [new file with mode: 0644]
src/libstd/rt/rtio.rs
src/libstd/rt/uv/mod.rs
src/libstd/rt/uv/signal.rs [new file with mode: 0644]
src/libstd/rt/uv/uvio.rs
src/libstd/rt/uv/uvll.rs
src/rt/rust_uv.cpp
src/rt/rustrt.def.in

index 7f5b01bb1ece9c94f82f9714daa7cde7e6450331..758c97791658f878094960a97298377f34bdd999 100644 (file)
@@ -326,6 +326,9 @@ pub mod unix { }
 /// 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 (file)
index 0000000..d3c260d
--- /dev/null
@@ -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);
+        }
+    }
+}
index 45c720a89b34c89eb06c17b126336a1ab9df33b7..528be59c54fc77be4d3d94e2b2a862cb28ca719f 100644 (file)
 use libc;
 use option::*;
 use result::*;
+use comm::SharedChan;
 use libc::c_int;
 use c_str::CString;
 
 use ai = rt::io::net::addrinfo;
 use rt::io::IoError;
+use rt::io::signal::Signum;
 use super::io::process::ProcessConfig;
 use super::io::net::ip::{IpAddr, SocketAddr};
 use path::Path;
@@ -100,6 +102,8 @@ fn unix_bind(&mut self, path: &CString) ->
     fn unix_connect(&mut self, path: &CString) -> Result<~RtioPipe, IoError>;
     fn tty_open(&mut self, fd: c_int, readable: bool)
             -> Result<~RtioTTY, IoError>;
+    fn signal(&mut self, signal: Signum, channel: SharedChan<Signum>)
+        -> Result<~RtioSignal, IoError>;
 }
 
 pub trait RtioTcpListener : RtioSocket {
@@ -192,3 +196,5 @@ pub trait PausibleIdleCallback {
     fn resume(&mut self);
     fn close(&mut self);
 }
+
+pub trait RtioSignal {}
index 88bb28d3763f09c6c173e867a37971623e670655..c92a54425bf4f2962ee08e4fde04527d93101bcb 100644 (file)
@@ -48,6 +48,7 @@
 use ptr::null;
 use unstable::finally::Finally;
 use rt::io::net::ip::SocketAddr;
+use rt::io::signal::Signum;
 
 use rt::io::IoError;
 
@@ -60,6 +61,7 @@
 pub use self::async::AsyncWatcher;
 pub use self::process::Process;
 pub use self::pipe::Pipe;
+pub use self::signal::SignalWatcher;
 
 /// The implementation of `rtio` for libuv
 pub mod uvio;
@@ -76,6 +78,7 @@
 pub mod process;
 pub mod pipe;
 pub mod tty;
+pub mod signal;
 
 /// XXX: Loop(*handle) is buggy with destructors. Normal structs
 /// with dtors may not be destructured, but tuple structs can,
@@ -146,6 +149,7 @@ fn native_handle(&self) -> *uvll::uv_loop_t {
 pub type AsyncCallback = ~fn(AsyncWatcher, Option<UvError>);
 pub type UdpReceiveCallback = ~fn(UdpWatcher, int, Buf, SocketAddr, uint, Option<UvError>);
 pub type UdpSendCallback = ~fn(UdpWatcher, Option<UvError>);
+pub type SignalCallback = ~fn(SignalWatcher, Signum);
 
 
 /// Callbacks used by StreamWatchers, set as custom data on the foreign handle.
@@ -162,6 +166,7 @@ struct WatcherData {
     udp_recv_cb: Option<UdpReceiveCallback>,
     udp_send_cb: Option<UdpSendCallback>,
     exit_cb: Option<ExitCallback>,
+    signal_cb: Option<SignalCallback>,
 }
 
 pub trait WatcherInterop {
@@ -197,6 +202,7 @@ fn install_watcher_data(&mut self) {
                 udp_recv_cb: None,
                 udp_send_cb: None,
                 exit_cb: None,
+                signal_cb: None,
             };
             let data = transmute::<~WatcherData, *c_void>(data);
             uvll::set_data_for_uv_handle(self.native_handle(), data);
diff --git a/src/libstd/rt/uv/signal.rs b/src/libstd/rt/uv/signal.rs
new file mode 100644 (file)
index 0000000..70ea8e3
--- /dev/null
@@ -0,0 +1,100 @@
+// 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 cast;
+use option::Some;
+use libc::{c_int, c_void};
+use result::{Err, Ok, Result};
+use rt::io::IoError;
+use rt::io::signal::Signum;
+use rt::uv::{Loop, NativeHandle, NullCallback, SignalCallback, UvError, Watcher};
+use rt::uv::uv_error_to_io_error;
+use rt::uv::uvll;
+
+pub struct SignalWatcher(*uvll::uv_signal_t);
+
+impl Watcher for SignalWatcher { }
+
+impl SignalWatcher {
+    pub fn new(loop_: &mut Loop) -> SignalWatcher {
+        unsafe {
+            let handle = uvll::malloc_handle(uvll::UV_SIGNAL);
+            assert!(handle.is_not_null());
+            assert!(0 == uvll::signal_init(loop_.native_handle(), handle));
+            let mut watcher: SignalWatcher = NativeHandle::from_native_handle(handle);
+            watcher.install_watcher_data();
+            return watcher;
+        }
+    }
+
+    pub fn start(&mut self, signum: Signum, callback: SignalCallback) -> Result<(), IoError> {
+        {
+            let data = self.get_watcher_data();
+            data.signal_cb = Some(callback);
+        }
+
+        let ret = unsafe {
+            uvll::signal_start(self.native_handle(), signal_cb, signum as c_int)
+        };
+
+        return match ret {
+            0 => Ok(()),
+            _ => Err(uv_error_to_io_error(UvError(ret))),
+        };
+
+        extern fn signal_cb(handle: *uvll::uv_signal_t, signum: c_int) {
+            let mut watcher: SignalWatcher = NativeHandle::from_native_handle(handle);
+            let data = watcher.get_watcher_data();
+            let cb = data.signal_cb.get_ref();
+            (*cb)(watcher, unsafe { cast::transmute(signum as i64) });
+        }
+    }
+
+    pub fn stop(&mut self) {
+        unsafe {
+            uvll::signal_stop(self.native_handle());
+        }
+    }
+
+    pub fn close(self, cb: NullCallback) {
+        let mut watcher = self;
+        {
+            let data = watcher.get_watcher_data();
+            assert!(data.close_cb.is_none());
+            data.close_cb = Some(cb);
+        }
+
+        unsafe {
+            uvll::close(watcher.native_handle(), close_cb);
+        }
+
+        extern fn close_cb(handle: *uvll::uv_signal_t) {
+            let mut watcher: SignalWatcher = NativeHandle::from_native_handle(handle);
+            {
+                let data = watcher.get_watcher_data();
+                data.close_cb.take_unwrap()();
+            }
+            watcher.drop_watcher_data();
+            unsafe {
+                uvll::free_handle(handle as *c_void);
+            }
+        }
+    }
+}
+
+impl NativeHandle<*uvll::uv_signal_t> for SignalWatcher {
+    fn from_native_handle(handle: *uvll::uv_signal_t) -> SignalWatcher {
+        SignalWatcher(handle)
+    }
+
+    fn native_handle(&self) -> *uvll::uv_signal_t {
+        match self { &SignalWatcher(ptr) => ptr }
+    }
+}
index f12b1bce9e67d91e6a19442348d930540c6c4852..48f4dbe43d6f4f0e2d3340bc3e008fd5287ed613 100644 (file)
@@ -13,6 +13,7 @@
 use cast;
 use cell::Cell;
 use clone::Clone;
+use comm::{SendDeferred, SharedChan};
 use libc::{c_int, c_uint, c_void, pid_t};
 use ops::Drop;
 use option::*;
@@ -40,6 +41,7 @@
 use rt::io::{FileMode, FileAccess, OpenOrCreate, Open, Create,
              CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite,
              FileStat};
+use rt::io::signal::Signum;
 use task;
 use ai = rt::io::net::addrinfo;
 
@@ -861,6 +863,17 @@ fn pipe_open(&mut self, fd: c_int) -> Result<~RtioPipe, IoError> {
             Err(e) => Err(uv_error_to_io_error(e))
         }
     }
+
+    fn signal(&mut self, signum: Signum, channel: SharedChan<Signum>)
+        -> Result<~RtioSignalObject, IoError> {
+        let watcher = SignalWatcher::new(self.uv_loop());
+        let home = get_handle_to_current_scheduler!();
+        let mut signal = ~UvSignal::new(watcher, home);
+        match signal.watcher.start(signum, |_, _| channel.send_deferred(signum)) {
+            Ok(()) => Ok(signal),
+            Err(e) => Err(uv_error_to_io_error(e)),
+        }
+    }
 }
 
 pub struct UvTcpListener {
@@ -1811,6 +1824,38 @@ fn dont_accept_simultaneously(&mut self) -> Result<(), IoError> {
     }
 }
 
+pub struct UvSignal {
+    watcher: signal::SignalWatcher,
+    home: SchedHandle,
+}
+
+impl HomingIO for UvSignal {
+    fn home<'r>(&'r mut self) -> &'r mut SchedHandle { &mut self.home }
+}
+
+impl UvSignal {
+    fn new(w: signal::SignalWatcher, home: SchedHandle) -> UvSignal {
+        UvSignal { watcher: w, home: home }
+    }
+}
+
+impl RtioSignal for UvSignal {}
+
+impl Drop for UvSignal {
+    fn drop(&mut self) {
+        do self.home_for_io_with_sched |self_, scheduler| {
+            rtdebug!("closing UvSignal");
+            do scheduler.deschedule_running_task_and_then |_, task| {
+                let task_cell = Cell::new(task);
+                do self_.watcher.close {
+                    let scheduler: ~Scheduler = Local::take();
+                    scheduler.resume_blocked_task_immediately(task_cell.take());
+                }
+            }
+        }
+    }
+}
+
 // this function is full of lies
 unsafe fn local_io() -> &'static mut IoFactory {
     do Local::borrow |sched: &mut Scheduler| {
index f3e14dc314b70eaaca9634cbfb2ad8d3e0b8b2c0..fa4083657d59125e95da05f7baa46c6344449a85 100644 (file)
@@ -132,6 +132,7 @@ pub struct uv_stdio_container_t {
 pub type uv_process_t = c_void;
 pub type uv_pipe_t = c_void;
 pub type uv_tty_t = c_void;
+pub type uv_signal_t = c_void;
 
 pub struct uv_timespec_t {
     tv_sec: libc::c_long,
@@ -219,6 +220,8 @@ pub fn is_dir(&self) -> bool {
 pub type uv_exit_cb = extern "C" fn(handle: *uv_process_t,
                                     exit_status: c_int,
                                     term_signal: c_int);
+pub type uv_signal_cb = extern "C" fn(handle: *uv_signal_t,
+                                      signum: c_int);
 
 pub type sockaddr = c_void;
 pub type sockaddr_in = c_void;
@@ -991,6 +994,21 @@ pub unsafe fn guess_handle(fd: c_int) -> uv_handle_type {
     rust_uv_guess_handle(fd)
 }
 
+pub unsafe fn signal_init(loop_: *uv_loop_t, handle: *uv_signal_t) -> c_int {
+    #[fixed_stack_segment]; #[inline(never)];
+    return rust_uv_signal_init(loop_, handle);
+}
+pub unsafe fn signal_start(handle: *uv_signal_t,
+                           signal_cb: uv_signal_cb,
+                           signum: c_int) -> c_int {
+    #[fixed_stack_segment]; #[inline(never)];
+    return rust_uv_signal_start(handle, signal_cb, signum);
+}
+pub unsafe fn signal_stop(handle: *uv_signal_t) -> c_int {
+    #[fixed_stack_segment]; #[inline(never)];
+    return rust_uv_signal_stop(handle);
+}
+
 pub struct uv_err_data {
     priv err_name: ~str,
     priv err_msg: ~str,
@@ -1160,4 +1178,10 @@ fn rust_uv_tty_get_winsize(tty: *uv_tty_t, width: *c_int,
     //#[rust_stack] pub fn rust_AI_NUMERICSERV() -> c_int;
     //#[rust_stack] pub fn rust_AI_PASSIVE() -> c_int;
     //#[rust_stack] pub fn rust_AI_V4MAPPED() -> c_int;
+
+    fn rust_uv_signal_init(loop_: *uv_loop_t, handle: *uv_signal_t) -> c_int;
+    fn rust_uv_signal_start(handle: *uv_signal_t,
+                            signal_cb: uv_signal_cb,
+                            signum: c_int) -> c_int;
+    fn rust_uv_signal_stop(handle: *uv_signal_t) -> c_int;
 }
index 55fa09c38183de1decea25c8d615e4e0889c387c..c463cf039d3061be537b01a027a202ced0c269a5 100644 (file)
@@ -673,3 +673,18 @@ extern "C" uv_handle_type
 rust_uv_guess_handle(int fd) {
     return uv_guess_handle(fd);
 }
+
+extern "C" int
+rust_uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) {
+  return uv_signal_init(loop, handle);
+}
+
+extern "C" int
+rust_uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) {
+  return uv_signal_start(handle, signal_cb, signum);
+}
+
+extern "C" int
+rust_uv_signal_stop(uv_signal_t* handle) {
+  return uv_signal_stop(handle);
+}
index 203f2e4d5f2b4833e7188a37ab61aa95a5e83d29..3c10e42f74384d1f2784d48680080b521340afc5 100644 (file)
@@ -191,6 +191,9 @@ rust_set_stdio_container_fd
 rust_set_stdio_container_stream
 rust_uv_process_pid
 rust_uv_pipe_init
+rust_uv_signal_init
+rust_uv_signal_start
+rust_uv_signal_stop
 sdhtml_renderer
 sd_markdown_new
 sd_markdown_render