wrapping libuv signal for use in Rust
descriptive names easier-to-use api reorganize and document
This commit is contained in:
parent
816e46dd63
commit
b5a02e0784
@ -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;
|
||||
|
||||
|
190
src/libstd/rt/io/signal.rs
Normal file
190
src/libstd/rt/io/signal.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,13 @@
|
||||
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 @@ pub trait IoFactory {
|
||||
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 {}
|
||||
|
@ -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;
|
||||
@ -76,6 +78,7 @@ pub mod addrinfo;
|
||||
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 @@ pub type TimerCallback = ~fn(TimerWatcher, Option<UvError>);
|
||||
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 @@ impl<H, W: Watcher + NativeHandle<*H>> 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);
|
||||
|
100
src/libstd/rt/uv/signal.rs
Normal file
100
src/libstd/rt/uv/signal.rs
Normal file
@ -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 }
|
||||
}
|
||||
}
|
@ -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::*;
|
||||
@ -40,6 +41,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;
|
||||
use ai = rt::io::net::addrinfo;
|
||||
|
||||
@ -861,6 +863,17 @@ impl IoFactory for UvIoFactory {
|
||||
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 @@ impl RtioUnixAcceptor for UvUnixAcceptor {
|
||||
}
|
||||
}
|
||||
|
||||
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| {
|
||||
|
@ -132,6 +132,7 @@ pub type uv_getaddrinfo_t = c_void;
|
||||
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 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;
|
||||
@ -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 @@ extern {
|
||||
//#[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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user