uninterruptible 0.1.0

Guard type that keeps selected Unix signals suppressed
Documentation
/*!
Guard type that keeps selected Unix signals suppressed.

```rust
use uninterruptible::Uninterruptible;

// catch terminate signals (SIGTERM, SIGQUIT, SIGINT)
let u = Uninterruptible::terminate().unwrap();

// do work, e.g. call in sub-process

u.checkpoint().unwrap(); // fails if the process recived one of the terminate signals (e.g. CTRL-C was pressed)


drop(u); // revert to default signal handlers
```
*/

use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use signal_hook::flag as signal_flag;
use std::io;

pub use signal_hook::consts::signal;
pub use signal_hook::consts::TERM_SIGNALS;

/// Guard type that keeps selected signals suppressed.
///
/// Note: When dropped an "emulated" default signal handler is installed instead.
pub struct Uninterruptible {
    terminate: Arc<AtomicUsize>,
    signals: Vec<i32>,
}

impl Uninterruptible {
    /// Makes program uninterruptible by terminate signals (SIGQUIT, SIGINT, SIGTERM) untill
    /// returned object is dropped.
    pub fn terminate() -> Result<Uninterruptible, io::Error> {
        Uninterruptible::signals(TERM_SIGNALS.iter().map(|s| *s).collect())
    }

    /// Makes program uninterruptible by SIGHUP signal untill returned object is dropped.
    pub fn hup() -> Result<Uninterruptible, io::Error> {
        Uninterruptible::signals(vec![signal::SIGHUP])
    }

    /// Makes program uninterruptible by terminate and hup signals.
    pub fn terminate_hup() -> Result<Uninterruptible, io::Error> {
        Uninterruptible::signals(TERM_SIGNALS.iter().map(|s| *s).chain(std::iter::once(signal::SIGHUP)).collect())
    }

    /// Makes program uninterruptible by given list of signals.
    pub fn signals(signals: Vec<i32>) -> Result<Uninterruptible, io::Error> {
        let terminate = Arc::new(AtomicUsize::new(0));

        for signal in &signals {
            signal_flag::register_usize(*signal, Arc::clone(&terminate), *signal as usize)?;
        }

        Ok(Uninterruptible {
            terminate,
            signals,
        })
    }

    /// Checks if there was a signal received and returns Err in that case.
    pub fn checkpoint(&self) -> Result<(), io::Error> {
        match self.terminate.load(Ordering::Relaxed) {
            0 => Ok(()),
            signal => {
                self.terminate.store(0, Ordering::Relaxed);
                Err(io::Error::new(io::ErrorKind::Interrupted, format!("Interrupted by the signal {}", signal)))
            }
        }
    }
}

impl Drop for Uninterruptible {
    fn drop(&mut self) {
        for signal in self.signals.drain(..) {
            // emulate defalt action; apparently there is no way to restore the default action
            signal_hook::flag::register_conditional_default(signal, Arc::new(AtomicBool::new(true))).unwrap();
        }
    }
}