From 7197fecc2e3c384d9b7199987e021832dc026ef9 Mon Sep 17 00:00:00 2001 From: Do Nhat Minh Date: Thu, 19 Sep 2013 12:03:50 +0800 Subject: [PATCH] wrapping libuv signal for use in Rust descriptive names easier-to-use api reorganize and document --- src/libstd/rt/io/mod.rs | 3 + src/libstd/rt/io/signal.rs | 190 +++++++++++++++++++++++++++++++++++++ src/libstd/rt/rtio.rs | 8 ++ src/libstd/rt/uv/mod.rs | 6 ++ src/libstd/rt/uv/signal.rs | 100 +++++++++++++++++++ src/libstd/rt/uv/uvio.rs | 54 +++++++++++ src/libstd/rt/uv/uvll.rs | 24 +++++ src/rt/rust_uv.cpp | 15 +++ src/rt/rustrt.def.in | 3 + 9 files changed, 403 insertions(+) create mode 100644 src/libstd/rt/io/signal.rs create mode 100644 src/libstd/rt/uv/signal.rs diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index d505c97ba0f9d..f8d5e61bfd182 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -331,6 +331,9 @@ pub mod native { /// Mock implementations for testing mod mock; +/// Signal handling +pub mod signal; + /// The default buffer size for various I/O operations /// XXX: Not pub pub 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 0000000000000..d3c260d361c63 --- /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 or the MIT license +// , 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, + /// chan is where all the handles send signums, which are received by + /// the clients from port. + priv chan: SharedChan, + /// Clients of Listener can `recv()` from this port + port: Port, +} + +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); + } + } +} diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index ca521c792dc73..889f4a378c8cc 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -11,9 +11,11 @@ use libc; use option::*; use result::*; +use comm::SharedChan; use libc::c_int; use rt::io::IoError; +use rt::io::signal::Signum; use super::io::process::ProcessConfig; use super::io::net::ip::{IpAddr, SocketAddr}; use rt::uv::uvio; @@ -36,6 +38,7 @@ pub type PausibleIdleCallback = uvio::UvPausibleIdleCallback; pub type RtioPipeObject = uvio::UvPipeStream; pub type RtioUnboundPipeObject = uvio::UvUnboundPipe; pub type RtioProcessObject = uvio::UvProcess; +pub type RtioSignalObject = uvio::UvSignal; pub trait EventLoop { fn run(&mut self); @@ -45,6 +48,8 @@ pub trait EventLoop { fn remote_callback(&mut self, ~fn()) -> ~RemoteCallbackObject; /// The asynchronous I/O services. Not all event loops may provide one fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject>; + fn signal(&mut self, signal: Signum, channel: SharedChan) + -> Result<~RtioSignalObject, IoError>; } pub trait RemoteCallback { @@ -87,6 +92,7 @@ pub trait IoFactory { fn pipe_init(&mut self, ipc: bool) -> Result<~RtioUnboundPipeObject, IoError>; fn spawn(&mut self, config: ProcessConfig) -> Result<(~RtioProcessObject, ~[Option]), IoError>; + fn signal_init(&mut self) -> Result<~RtioSignalObject, IoError>; } pub trait RtioTcpListener : RtioSocket { @@ -154,3 +160,5 @@ pub trait RtioPipe { fn read(&mut self, buf: &mut [u8]) -> Result; fn write(&mut self, buf: &[u8]) -> Result<(), IoError>; } + +pub trait RtioSignal {} diff --git a/src/libstd/rt/uv/mod.rs b/src/libstd/rt/uv/mod.rs index 67926b35a6219..65de85474fb31 100644 --- a/src/libstd/rt/uv/mod.rs +++ b/src/libstd/rt/uv/mod.rs @@ -48,6 +48,7 @@ use cast::transmute; 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::timer::TimerWatcher; 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; @@ -75,6 +77,7 @@ pub mod async; pub mod addrinfo; pub mod process; pub mod pipe; +pub mod signal; /// XXX: Loop(*handle) is buggy with destructors. Normal structs /// with dtors may not be destructured, but tuple structs can, @@ -137,6 +140,7 @@ pub type TimerCallback = ~fn(TimerWatcher, Option); pub type AsyncCallback = ~fn(AsyncWatcher, Option); pub type UdpReceiveCallback = ~fn(UdpWatcher, int, Buf, SocketAddr, uint, Option); pub type UdpSendCallback = ~fn(UdpWatcher, Option); +pub type SignalCallback = ~fn(SignalWatcher, Signum); /// Callbacks used by StreamWatchers, set as custom data on the foreign handle. @@ -153,6 +157,7 @@ struct WatcherData { udp_recv_cb: Option, udp_send_cb: Option, exit_cb: Option, + signal_cb: Option, } pub trait WatcherInterop { @@ -186,6 +191,7 @@ impl> WatcherInterop for W { 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 index 0000000000000..70ea8e399d587 --- /dev/null +++ b/src/libstd/rt/uv/signal.rs @@ -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 or the MIT license +// , 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 } + } +} diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index d5893d6d01414..5d1091f9268a9 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -13,6 +13,7 @@ use cast::transmute; 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::*; @@ -42,6 +43,7 @@ use libc::{lseek, off_t, O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY, use rt::io::{FileMode, FileAccess, OpenOrCreate, Open, Create, CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite, FileStat}; +use rt::io::signal::Signum; use task; #[cfg(test)] use container::Container; @@ -237,6 +239,20 @@ impl EventLoop for UvEventLoop { fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject> { Some(&mut self.uvio) } + + fn signal(&mut self, signum: Signum, channel: SharedChan) + -> Result<~RtioSignalObject, IoError> { + match self.uvio.signal_init() { + Ok(uv) => { + let mut w = uv; + match w.watcher.start(signum, |_, _| channel.send_deferred(signum)) { + Ok(*) => Ok(w), + Err(e) => Err(e), + } + }, + Err(e) => Err(e), + } + } } pub struct UvPausibleIdleCallback { @@ -798,6 +814,12 @@ impl IoFactory for UvIoFactory { } } } + + fn signal_init(&mut self) -> Result<~RtioSignalObject, IoError> { + let watcher = SignalWatcher::new(self.uv_loop()); + let home = get_handle_to_current_scheduler!(); + Ok(~UvSignal::new(watcher, home)) + } } pub struct UvTcpListener { @@ -1609,6 +1631,38 @@ impl RtioProcess for UvProcess { } } +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()); + } + } + } + } +} + #[test] fn test_simple_io_no_connect() { do run_in_mt_newsched_task { diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index a80d5cbc1fb43..2026c4b150b6b 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -131,6 +131,7 @@ pub type uv_udp_send_t = c_void; pub type uv_getaddrinfo_t = c_void; pub type uv_process_t = c_void; pub type uv_pipe_t = c_void; +pub type uv_signal_t = c_void; pub struct uv_timespec_t { tv_sec: libc::c_long, @@ -218,6 +219,8 @@ pub type uv_getaddrinfo_cb = extern "C" fn(req: *uv_getaddrinfo_t, 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; @@ -959,6 +962,21 @@ pub unsafe fn freeaddrinfo(ai: *addrinfo) { rust_uv_freeaddrinfo(ai); } +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 { err_name: ~str, err_msg: ~str, @@ -1102,4 +1120,10 @@ extern { fn rust_set_stdio_container_stream(c: *uv_stdio_container_t, stream: *uv_stream_t); fn rust_uv_pipe_init(loop_ptr: *c_void, p: *uv_pipe_t, ipc: c_int) -> 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; } diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index 3e9b8ba136eaa..005ce5e4d615a 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -637,3 +637,18 @@ extern "C" int rust_uv_pipe_init(uv_loop_t *loop, uv_pipe_t* p, int ipc) { return uv_pipe_init(loop, p, ipc); } + +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); +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 7323397508e2e..0592524e34663 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -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