use std::future::Future;
use tokio::{
select,
signal::unix::{signal, SignalKind},
task::JoinHandle,
};
use tokio_util::{sync::CancellationToken, task::TaskTracker};
pub async fn should_die() {
let mut sigint = signal(SignalKind::interrupt()).expect("Failed to listen to SIGINT");
let mut sigterm = signal(SignalKind::terminate()).expect("Failed to listen to SIGTERM");
select! {
_ = sigint.recv() => tracing::warn!("Got ^C signal!"),
_ = sigterm.recv() => tracing::warn!("Got SIGTERM signal!"),
}
}
#[derive(Clone, Debug, Default)]
pub struct Supervisor {
token: CancellationToken,
tasks: TaskTracker,
}
#[test]
fn assert_all() {
fn assert_clone<T: Clone>() {}
fn assert_send<T: Send>() {}
fn assert_sized<T: Sized>() {}
fn assert_sync<T: Sync>() {}
assert_clone::<Supervisor>();
assert_send::<Supervisor>();
assert_sized::<Supervisor>();
assert_sync::<Supervisor>();
}
impl Supervisor {
#[inline]
#[track_caller]
pub fn spawn<F, G>(&self, task: G) -> JoinHandle<F::Output>
where
G: FnOnce(Self) -> F,
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let this = self.clone();
self.tasks.spawn(task(this))
}
#[inline]
#[track_caller]
pub fn spawn_blocking<F, T>(&self, task: F) -> JoinHandle<T>
where
F: FnOnce(Self) -> T,
F: Send + 'static,
T: Send + 'static,
{
let this = self.clone();
self.tasks.spawn_blocking(|| task(this))
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.token.is_cancelled()
}
#[allow(clippy::manual_async_fn)] pub fn done(&self) -> impl Future<Output = ()> + Send + '_ {
async move {
let _: () = self.token.child_token().cancelled().await;
tracing::warn!("Children cancelled");
}
}
#[allow(clippy::manual_async_fn)] pub fn die(&self) -> impl Future<Output = ()> + Send + '_ {
async move {
tracing::warn!("Terminating...");
self.tasks.close();
self.token.cancel();
self.tasks.wait().await;
tracing::warn!("Terminated!");
}
}
}