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 a call to notify/notify_on (or block), the given signals are set to blocked. This is necessary for synchronous signal handling using sigwait.

After the first call to notify (or notify_on'), a new thread is spawned and immediately blocks on a call tosigwait. It is only unblocked when one of the signals that were masked previously by calls tonotifyetc. arrives, which now cannot be delivered directly to any of the threads of the process, and therefore unblocks the waiting signal watcher thread. 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 onsigwait` 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 (or notify_on). This is so all spawned threads inherit the blocked status of signals. If a thread starts before notify 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 one sigwait 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

block

Block all given signals without receiving notifications.

block_all_subscribable

Block all subscribable signals.

notify

Create a new channel subscribed to the given signals.

notify_on

Subscribe to a signal on a channel.