rostrum 8.0.0

An efficient implementation of Electrum Server with token support
Documentation
use crossbeam_channel as channel;
use crossbeam_channel::RecvTimeoutError;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;

use anyhow::Result;

static GLOBAL_SHUTDOWN_SIGNAL: AtomicBool = AtomicBool::new(false);

#[derive(Clone)] // so multiple threads could wait on signals
pub struct Waiter {
    receiver: channel::Receiver<i32>,
}

#[cfg(not(windows))]
fn notify(signals: &[i32]) -> channel::Receiver<i32> {
    use crate::thread::spawn;
    use anyhow::Context;
    let (s, r) = channel::bounded(1);
    let mut signals =
        signal_hook::iterator::Signals::new(signals).expect("failed to register signal hook");
    spawn("signal", move || {
        for signal in signals.forever() {
            s.send(signal)
                .context(anyhow!("failed to send signal {}", signal))?;
        }
        Ok(())
    });
    r
}

#[cfg(windows)]
fn notify(_signals: &[i32]) -> channel::Receiver<i32> {
    let (_, dummy) = channel::bounded(1);
    dummy
}

impl Waiter {
    #[cfg(windows)]
    pub fn start() -> Waiter {
        Waiter {
            receiver: notify(&[]),
        }
    }
    #[cfg(not(windows))]
    pub fn start() -> Waiter {
        Waiter {
            receiver: notify(&[
                signal_hook::consts::SIGINT,
                signal_hook::consts::SIGTERM,
                signal_hook::consts::SIGUSR1, // allow external triggering (e.g. via bitcoind `blocknotify`)
            ]),
        }
    }
    pub fn shutdown_check() -> Result<()> {
        if GLOBAL_SHUTDOWN_SIGNAL.load(Ordering::Relaxed) {
            bail!("Shutdown triggered");
        }
        Ok(())
    }
    pub fn wait(&self, duration: Duration) -> Result<()> {
        Self::shutdown_check()?;
        match self.receiver.recv_timeout(duration) {
            Ok(sig) => {
                info!("notified via SIG{}", sig);
                #[cfg(not(windows))]
                if sig != signal_hook::consts::SIGUSR1 {
                    GLOBAL_SHUTDOWN_SIGNAL.store(true, Ordering::SeqCst);
                    bail!("Interrupted by signal {}", sig)
                };
                Ok(())
            }
            Err(RecvTimeoutError::Timeout) => Ok(()),
            Err(RecvTimeoutError::Disconnected) => bail!("signal hook channel disconnected"),
        }
    }
    pub fn poll(&self) -> Result<()> {
        self.wait(Duration::from_secs(0))
    }
}