-
Notifications
You must be signed in to change notification settings - Fork 13.3k
wrapping libuv signal for use in Rust #9318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why the transmute? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
|
||
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 } | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add a test where you explicitly
unregister
a signal and make sure that nothing is received on that channel? You'll probably have to have two listeners active (so the signal doesn't kill the process) and only unregister one of them.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test added here https://github.com/mozilla/rust/pull/9318/files#diff-998a059252a90f32c0986d7f26cf8430R169