Crate chan_signal [−] [src]
This crate provides a simplistic interface to subscribe to operating system signals through a channel API. Use is extremely simple:
use chan_signal::Signal; let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]); // Blocks until this process is sent an INT or TERM signal. // Since the channel is never closed, we can unwrap the received value. signal.recv().unwrap();
Example
When combined with chan_select!
from the chan
crate, one can easily
integrate signals with the rest of your program. For example, consider a
main function that waits for either normal completion of work (which is done
in a separate thread) or for a signal to be delivered:
#[macro_use] extern crate chan; extern crate chan_signal; use chan_signal::Signal; fn main() { // Signal gets a value when the OS sent a INT or TERM signal. let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]); // When our work is complete, send a sentinel value on `sdone`. let (sdone, rdone) = chan::sync(0); // Run work. ::std::thread::spawn(move || run(sdone)); // Wait for a signal or for work to be done. chan_select! { signal.recv() -> signal => { println!("received signal: {:?}", signal) }, rdone.recv() => { println!("Program completed normally."); } } } fn run(_sdone: chan::Sender<()>) { // Do some work. ::std::thread::sleep_ms(1000); // Quit normally. // Note that we don't need to send any values. We just let the // sending channel drop, which closes the channel, which causes // the receiver to synchronize immediately and always. }
You can see this example in action by running cargo run --example select
in the root directory of this crate's
repository.
Platform support (no Windows support)
This should work on Unix platforms supported by Rust itself.
There is no Windows support at all. I welcome others to either help me add it or help educate me so that I may one day add it.
How it works
Overview: uses the "spawn a thread and block on sigwait
" approach. In
particular, it avoids standard asynchronous signal handling because it is
very difficult to do anything non-trivial inside a signal handler.
After the first call to notify
(or notify_on
), all signals defined in the
Signal
enum are set to blocked. This is necessary for synchronous signal
handling using sigwait
.
After the signals are blocked, a new thread is spawned and immediately blocks
on a call to sigwait
. It is only unblocked when one of the signals in
the Signal
enum are sent to the process. Once it's unblocked, it sends the
signal on all subscribed channels via a non-blocking send. Once all channels
have been visited, the thread blocks on sigwait
again.
This approach has some restrictions. Namely, your program must comply with the following:
- Any and all threads spawned in your program must come after the first
call to
notify
(ornotify_on
). This is so all spawned threads inherit the blocked status of signals. If a thread starts beforenotify
is called, it will not have the correct signal mask. When a signal is delivered, the result is indeterminate. - No other threads may call
sigwait
. When a signal is delivered, only onesigwait
is indeterminately unblocked.
Future work
This crate exposes the simplest API I could think of. As a result, a few additions may be warranted:
- Expand the set of signals. (Requires figuring out platform differences.)
- Allow channel unsubscription.
- Allow callers to reset the signal mask? (Seems hard.)
- Support Windows.
Enums
Signal |
The set of subscribable signals. |
Functions
notify |
Create a new channel subscribed to the given signals. |
notify_on |
Subscribe to a signal on a channel. |