use crate::{
ProcFuture, ProcessControlHandler, ProcessOperation, Runnable, RuntimeControlMessage,
RuntimeError, RuntimeGuard,
};
use futures::stream::StreamExt as _;
use signal_hook::consts::signal::*;
use signal_hook::iterator::Handle;
use signal_hook_tokio::{Signals, SignalsInfo};
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct SignalReceiver {
signals: Mutex<SignalsInfo>,
signal_handle: Handle,
runtime_guard: RuntimeGuard,
}
impl SignalReceiver {
pub fn new() -> Self {
let signals = Signals::new([SIGHUP, SIGINT, SIGTERM, SIGQUIT])
.map_err(|err| RuntimeError::Internal {
message: format!("register signal handler: {err:?}"),
})
.expect("signals to register");
let handle = signals.handle();
Self {
signals: Mutex::new(signals),
signal_handle: handle,
runtime_guard: RuntimeGuard::default(),
}
}
}
impl Default for SignalReceiver {
fn default() -> Self {
Self::new()
}
}
impl Runnable for SignalReceiver {
fn process_start(&self) -> ProcFuture<'_> {
Box::pin(async {
let mut signals = self.signals.lock().await;
let ticker = self.runtime_guard.runtime_ticker().await;
loop {
let signal = match ticker.tick(signals.next()).await {
ProcessOperation::Next(None) => continue,
ProcessOperation::Next(Some(signal)) => signal,
ProcessOperation::Control(RuntimeControlMessage::Shutdown) => break,
ProcessOperation::Control(_) => continue,
};
#[cfg(feature = "tracing")]
::tracing::warn!(signal = ?signal, "Received process signal");
match signal {
SIGHUP => {
}
SIGTERM | SIGINT | SIGQUIT => {
self.signal_handle.close();
return Err(RuntimeError::TerminationSignal);
}
_ => unreachable!(),
}
}
Ok(())
})
}
fn process_handle(&self) -> Arc<dyn ProcessControlHandler> {
self.runtime_guard.handle()
}
fn process_name(&self) -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Borrowed("SignalReceiver")
}
}