cargo-binstall/crates/bin/src/signal.rs
Jiahao XU 2375ba48b6
Refactor to reduce compilation time (#1284)
- Make `binstalk::bins` private.
 - Move mod `signal` into `crates/bin`
 - Make items in `crates/bin/src/lib.rs` private if possible to reduce
   its API generation time.

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
2023-08-12 14:25:41 +00:00

84 lines
2.4 KiB
Rust

use std::io;
use binstalk::{errors::BinstallError, helpers::tasks::AutoAbortJoinHandle};
use tokio::signal;
/// This function will poll the handle while listening for ctrl_c,
/// `SIGINT`, `SIGHUP`, `SIGTERM` and `SIGQUIT`.
///
/// When signal is received, [`BinstallError::UserAbort`] will be returned.
///
/// It would also ignore `SIGUSER1` and `SIGUSER2` on unix.
///
/// This function uses [`tokio::signal`] and once exit, does not reset the default
/// signal handler, so be careful when using it.
pub async fn cancel_on_user_sig_term<T>(
handle: AutoAbortJoinHandle<T>,
) -> Result<T, BinstallError> {
ignore_signals()?;
tokio::select! {
biased;
res = wait_on_cancellation_signal() => {
res.map_err(BinstallError::Io)
.and(Err(BinstallError::UserAbort))
}
res = handle => res,
}
}
fn ignore_signals() -> io::Result<()> {
#[cfg(unix)]
unix::ignore_signals_on_unix()?;
Ok(())
}
/// If call to it returns `Ok(())`, then all calls to this function after
/// that also returns `Ok(())`.
async fn wait_on_cancellation_signal() -> Result<(), io::Error> {
#[cfg(unix)]
unix::wait_on_cancellation_signal_unix().await?;
#[cfg(not(unix))]
signal::ctrl_c().await?;
Ok(())
}
#[cfg(unix)]
mod unix {
use super::*;
use signal::unix::{signal, SignalKind};
/// Same as [`wait_on_cancellation_signal`] but is only available on unix.
pub async fn wait_on_cancellation_signal_unix() -> Result<(), io::Error> {
tokio::select! {
biased;
res = wait_for_signal_unix(SignalKind::interrupt()) => res,
res = wait_for_signal_unix(SignalKind::hangup()) => res,
res = wait_for_signal_unix(SignalKind::terminate()) => res,
res = wait_for_signal_unix(SignalKind::quit()) => res,
}
}
/// Wait for first arrival of signal.
pub async fn wait_for_signal_unix(kind: signal::unix::SignalKind) -> Result<(), io::Error> {
let mut sig_listener = signal::unix::signal(kind)?;
if sig_listener.recv().await.is_some() {
Ok(())
} else {
// Use pending() here for the same reason as above.
std::future::pending().await
}
}
pub fn ignore_signals_on_unix() -> Result<(), io::Error> {
drop(signal(SignalKind::user_defined1())?);
drop(signal(SignalKind::user_defined2())?);
Ok(())
}
}