use std::future::Future;
use tokio::{
select,
signal::unix::{signal, SignalKind},
task::{futures::TaskLocalFuture, JoinHandle},
};
use tokio_util::{sync::CancellationToken, task::TaskTracker};
use tracing::warn;
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() => warn!("Got ^C signal!"),
_ = sigterm.recv() => warn!("Got SIGTERM signal!"),
}
}
tokio::task_local! {
static SUPERVIZED: Supervisor;
}
#[inline]
pub fn current() -> Option<Supervisor> {
SUPERVIZED.try_with(Clone::clone).ok()
}
pub fn supervized<F>(f: F) -> TaskLocalFuture<Supervisor, F>
where
F: Future,
{
let slf = current().unwrap_or_default();
SUPERVIZED.scope(slf, f)
}
#[track_caller]
pub fn sync_supervized<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let slf = current().unwrap_or_default();
SUPERVIZED.sync_scope(slf, f)
}
#[inline]
#[track_caller]
pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let slf = current().expect(REASON);
slf.spawn(future)
}
#[track_caller]
pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let slf = current().expect(REASON);
slf.spawn_blocking(f)
}
#[must_use]
pub fn is_cancelled() -> bool {
let slf = current().expect(REASON);
slf.is_cancelled()
}
pub async fn done() {
let slf = current().expect(REASON);
slf.done().await;
}
pub async fn terminate() {
let slf = current().expect(REASON);
slf.terminate().await;
}
#[derive(Clone, Debug, Default)]
pub struct Supervisor {
token: CancellationToken,
tasks: TaskTracker,
}
#[test]
fn assert_all() {
fn asserts<T: Clone + Sized + Send + Sync>() {}
asserts::<Supervisor>();
}
const REASON: &str = "Not running within supervized(async move { .. }) or sync_supervized(|| ..)";
impl Supervisor {
#[inline]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
self.tasks.spawn(future)
}
#[inline]
#[track_caller]
pub fn spawn_blocking<F, R>(&self, f: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
self.tasks.spawn_blocking(f)
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.token.is_cancelled()
}
pub async fn done(&self) {
let _: () = self.token.child_token().cancelled().await;
tracing::warn!("Children cancelled");
}
pub async fn terminate(&self) {
tracing::warn!("Terminating...");
self.tasks.close();
self.token.cancel();
self.tasks.wait().await;
tracing::warn!("Terminated!");
}
}