Skip to content

Commit b5a02e0

Browse files
Do Nhat Minhalexcrichton
authored andcommitted
wrapping libuv signal for use in Rust
descriptive names easier-to-use api reorganize and document
1 parent 816e46d commit b5a02e0

File tree

9 files changed

+392
-0
lines changed

9 files changed

+392
-0
lines changed

src/libstd/rt/io/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ pub mod native {
326326
/// Mock implementations for testing
327327
mod mock;
328328

329+
/// Signal handling
330+
pub mod signal;
331+
329332
/// The default buffer size for various I/O operations
330333
static DEFAULT_BUF_SIZE: uint = 1024 * 64;
331334

src/libstd/rt/io/signal.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use comm::{Port, SharedChan, stream};
12+
use hashmap;
13+
use option::{Some, None};
14+
use result::{Err, Ok};
15+
use rt::io::io_error;
16+
use rt::local::Local;
17+
use rt::rtio::{EventLoop, RtioSignalObject};
18+
use rt::sched::Scheduler;
19+
20+
#[deriving(Eq, IterBytes)]
21+
pub enum Signum {
22+
/// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
23+
Break = 21i,
24+
/// Equivalent to SIGHUP, delivered when the user closes the terminal
25+
/// window. On delivery of HangUp, the program is given approximately
26+
/// 10 seconds to perfom any cleanup. After that, Windows will
27+
/// unconditionally terminate it.
28+
HangUp = 1i,
29+
/// Equivalent to SIGINT, delivered when the user presses Ctrl-c.
30+
Interrupt = 2i,
31+
/// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\.
32+
Quit = 3i,
33+
/// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z.
34+
StopTemporarily = 20i,
35+
/// Equivalent to SIGUSR1.
36+
User1 = 10i,
37+
/// Equivalent to SIGUSR2.
38+
User2 = 12i,
39+
/// Equivalent to SIGWINCH, delivered when the console has been resized.
40+
/// WindowSizeChange may not be delivered in a timely manner; size change
41+
/// will only be detected when the cursor is being moved.
42+
WindowSizeChange = 28i,
43+
}
44+
45+
/// Listener provides a port to listen for registered signals.
46+
///
47+
/// Listener automatically unregisters its handles once it is out of scope.
48+
/// However, clients can still unregister signums manually.
49+
///
50+
/// Example usage:
51+
///
52+
/// ```rust
53+
/// use std::rt::io::signal;
54+
/// use std::task;
55+
///
56+
/// let mut listener = signal::Listener();
57+
/// listener.register(signal::Interrupt);
58+
///
59+
/// do task::spawn {
60+
/// loop {
61+
/// match listener.recv() {
62+
/// signal::Interrupt => println("Got Interrupt'ed"),
63+
/// _ => (),
64+
/// }
65+
/// }
66+
/// }
67+
///
68+
/// ```
69+
pub struct Listener {
70+
/// A map from signums to handles to keep the handles in memory
71+
priv handles: hashmap::HashMap<Signum, ~RtioSignalObject>,
72+
/// chan is where all the handles send signums, which are received by
73+
/// the clients from port.
74+
priv chan: SharedChan<Signum>,
75+
/// Clients of Listener can `recv()` from this port
76+
port: Port<Signum>,
77+
}
78+
79+
impl Listener {
80+
pub fn new() -> Listener {
81+
let (port, chan) = stream();
82+
Listener {
83+
chan: SharedChan::new(chan),
84+
port: port,
85+
handles: hashmap::HashMap::new(),
86+
}
87+
}
88+
89+
/// Listen for a signal, returning true when successfully registered for
90+
/// signum. Signals can be received using `recv()`.
91+
pub fn register(&mut self, signum: Signum) -> bool {
92+
match self.handles.find(&signum) {
93+
Some(_) => true, // self is already listening to signum, so succeed
94+
None => {
95+
let chan = self.chan.clone();
96+
let handle = unsafe {
97+
rtdebug!("Listener::register: borrowing io to init UvSignal");
98+
let sched: *mut Scheduler = Local::unsafe_borrow();
99+
rtdebug!("about to init handle");
100+
(*sched).event_loop.signal(signum, chan)
101+
};
102+
match handle {
103+
Ok(w) => {
104+
self.handles.insert(signum, w);
105+
true
106+
},
107+
Err(ioerr) => {
108+
rtdebug!("Listener::register: failed to init: {:?}", ioerr);
109+
io_error::cond.raise(ioerr);
110+
false
111+
},
112+
}
113+
},
114+
}
115+
}
116+
117+
/// Unregister a signal.
118+
pub fn unregister(&mut self, signum: Signum) {
119+
self.handles.pop(&signum);
120+
}
121+
}
122+
123+
#[cfg(test)]
124+
mod test {
125+
use libc;
126+
use rt::io::timer;
127+
use super::*;
128+
129+
// kill is only available on Unixes
130+
#[cfg(unix)]
131+
#[fixed_stack_segment]
132+
fn sigint() {
133+
unsafe {
134+
libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT);
135+
}
136+
}
137+
138+
#[test]
139+
fn test_io_signal_smoketest() {
140+
let mut signal = Listener::new();
141+
signal.register(Interrupt);
142+
sigint();
143+
timer::sleep(10);
144+
match signal.port.recv() {
145+
Interrupt => (),
146+
s => fail2!("Expected Interrupt, got {:?}", s),
147+
}
148+
}
149+
150+
#[test]
151+
fn test_io_signal_two_signal_one_signum() {
152+
let mut s1 = Listener::new();
153+
let mut s2 = Listener::new();
154+
s1.register(Interrupt);
155+
s2.register(Interrupt);
156+
sigint();
157+
timer::sleep(10);
158+
match s1.port.recv() {
159+
Interrupt => (),
160+
s => fail2!("Expected Interrupt, got {:?}", s),
161+
}
162+
match s1.port.recv() {
163+
Interrupt => (),
164+
s => fail2!("Expected Interrupt, got {:?}", s),
165+
}
166+
}
167+
168+
#[test]
169+
fn test_io_signal_unregister() {
170+
let mut s1 = Listener::new();
171+
let mut s2 = Listener::new();
172+
s1.register(Interrupt);
173+
s2.register(Interrupt);
174+
s2.unregister(Interrupt);
175+
sigint();
176+
timer::sleep(10);
177+
if s2.port.peek() {
178+
fail2!("Unexpected {:?}", s2.port.recv());
179+
}
180+
}
181+
182+
#[cfg(windows)]
183+
#[test]
184+
fn test_io_signal_invalid_signum() {
185+
let mut s = Listener::new();
186+
if s.register(User1) {
187+
fail2!("Unexpected successful registry of signum {:?}", User1);
188+
}
189+
}
190+
}

src/libstd/rt/rtio.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
use libc;
1212
use option::*;
1313
use result::*;
14+
use comm::SharedChan;
1415
use libc::c_int;
1516
use c_str::CString;
1617

1718
use ai = rt::io::net::addrinfo;
1819
use rt::io::IoError;
20+
use rt::io::signal::Signum;
1921
use super::io::process::ProcessConfig;
2022
use super::io::net::ip::{IpAddr, SocketAddr};
2123
use path::Path;
@@ -100,6 +102,8 @@ pub trait IoFactory {
100102
fn unix_connect(&mut self, path: &CString) -> Result<~RtioPipe, IoError>;
101103
fn tty_open(&mut self, fd: c_int, readable: bool)
102104
-> Result<~RtioTTY, IoError>;
105+
fn signal(&mut self, signal: Signum, channel: SharedChan<Signum>)
106+
-> Result<~RtioSignal, IoError>;
103107
}
104108

105109
pub trait RtioTcpListener : RtioSocket {
@@ -192,3 +196,5 @@ pub trait PausibleIdleCallback {
192196
fn resume(&mut self);
193197
fn close(&mut self);
194198
}
199+
200+
pub trait RtioSignal {}

src/libstd/rt/uv/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use cast::transmute;
4848
use ptr::null;
4949
use unstable::finally::Finally;
5050
use rt::io::net::ip::SocketAddr;
51+
use rt::io::signal::Signum;
5152

5253
use rt::io::IoError;
5354

@@ -60,6 +61,7 @@ pub use self::timer::TimerWatcher;
6061
pub use self::async::AsyncWatcher;
6162
pub use self::process::Process;
6263
pub use self::pipe::Pipe;
64+
pub use self::signal::SignalWatcher;
6365

6466
/// The implementation of `rtio` for libuv
6567
pub mod uvio;
@@ -76,6 +78,7 @@ pub mod addrinfo;
7678
pub mod process;
7779
pub mod pipe;
7880
pub mod tty;
81+
pub mod signal;
7982

8083
/// XXX: Loop(*handle) is buggy with destructors. Normal structs
8184
/// with dtors may not be destructured, but tuple structs can,
@@ -146,6 +149,7 @@ pub type TimerCallback = ~fn(TimerWatcher, Option<UvError>);
146149
pub type AsyncCallback = ~fn(AsyncWatcher, Option<UvError>);
147150
pub type UdpReceiveCallback = ~fn(UdpWatcher, int, Buf, SocketAddr, uint, Option<UvError>);
148151
pub type UdpSendCallback = ~fn(UdpWatcher, Option<UvError>);
152+
pub type SignalCallback = ~fn(SignalWatcher, Signum);
149153

150154

151155
/// Callbacks used by StreamWatchers, set as custom data on the foreign handle.
@@ -162,6 +166,7 @@ struct WatcherData {
162166
udp_recv_cb: Option<UdpReceiveCallback>,
163167
udp_send_cb: Option<UdpSendCallback>,
164168
exit_cb: Option<ExitCallback>,
169+
signal_cb: Option<SignalCallback>,
165170
}
166171

167172
pub trait WatcherInterop {
@@ -197,6 +202,7 @@ impl<H, W: Watcher + NativeHandle<*H>> WatcherInterop for W {
197202
udp_recv_cb: None,
198203
udp_send_cb: None,
199204
exit_cb: None,
205+
signal_cb: None,
200206
};
201207
let data = transmute::<~WatcherData, *c_void>(data);
202208
uvll::set_data_for_uv_handle(self.native_handle(), data);

src/libstd/rt/uv/signal.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use cast;
12+
use option::Some;
13+
use libc::{c_int, c_void};
14+
use result::{Err, Ok, Result};
15+
use rt::io::IoError;
16+
use rt::io::signal::Signum;
17+
use rt::uv::{Loop, NativeHandle, NullCallback, SignalCallback, UvError, Watcher};
18+
use rt::uv::uv_error_to_io_error;
19+
use rt::uv::uvll;
20+
21+
pub struct SignalWatcher(*uvll::uv_signal_t);
22+
23+
impl Watcher for SignalWatcher { }
24+
25+
impl SignalWatcher {
26+
pub fn new(loop_: &mut Loop) -> SignalWatcher {
27+
unsafe {
28+
let handle = uvll::malloc_handle(uvll::UV_SIGNAL);
29+
assert!(handle.is_not_null());
30+
assert!(0 == uvll::signal_init(loop_.native_handle(), handle));
31+
let mut watcher: SignalWatcher = NativeHandle::from_native_handle(handle);
32+
watcher.install_watcher_data();
33+
return watcher;
34+
}
35+
}
36+
37+
pub fn start(&mut self, signum: Signum, callback: SignalCallback) -> Result<(), IoError> {
38+
{
39+
let data = self.get_watcher_data();
40+
data.signal_cb = Some(callback);
41+
}
42+
43+
let ret = unsafe {
44+
uvll::signal_start(self.native_handle(), signal_cb, signum as c_int)
45+
};
46+
47+
return match ret {
48+
0 => Ok(()),
49+
_ => Err(uv_error_to_io_error(UvError(ret))),
50+
};
51+
52+
extern fn signal_cb(handle: *uvll::uv_signal_t, signum: c_int) {
53+
let mut watcher: SignalWatcher = NativeHandle::from_native_handle(handle);
54+
let data = watcher.get_watcher_data();
55+
let cb = data.signal_cb.get_ref();
56+
(*cb)(watcher, unsafe { cast::transmute(signum as i64) });
57+
}
58+
}
59+
60+
pub fn stop(&mut self) {
61+
unsafe {
62+
uvll::signal_stop(self.native_handle());
63+
}
64+
}
65+
66+
pub fn close(self, cb: NullCallback) {
67+
let mut watcher = self;
68+
{
69+
let data = watcher.get_watcher_data();
70+
assert!(data.close_cb.is_none());
71+
data.close_cb = Some(cb);
72+
}
73+
74+
unsafe {
75+
uvll::close(watcher.native_handle(), close_cb);
76+
}
77+
78+
extern fn close_cb(handle: *uvll::uv_signal_t) {
79+
let mut watcher: SignalWatcher = NativeHandle::from_native_handle(handle);
80+
{
81+
let data = watcher.get_watcher_data();
82+
data.close_cb.take_unwrap()();
83+
}
84+
watcher.drop_watcher_data();
85+
unsafe {
86+
uvll::free_handle(handle as *c_void);
87+
}
88+
}
89+
}
90+
}
91+
92+
impl NativeHandle<*uvll::uv_signal_t> for SignalWatcher {
93+
fn from_native_handle(handle: *uvll::uv_signal_t) -> SignalWatcher {
94+
SignalWatcher(handle)
95+
}
96+
97+
fn native_handle(&self) -> *uvll::uv_signal_t {
98+
match self { &SignalWatcher(ptr) => ptr }
99+
}
100+
}

0 commit comments

Comments
 (0)