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:

Future work

This crate exposes the simplest API I could think of. As a result, a few additions may be warranted:

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.

Implementations