use std::future::Future;
use std::time::Duration;
use tokio_util::task::TaskTracker;
use crate::Shutdown;
#[derive(Debug, Clone)]
pub struct BackgroundTasks {
tracker: TaskTracker,
shutdown: Shutdown,
}
impl BackgroundTasks {
pub fn new(shutdown: Shutdown) -> Self {
Self {
tracker: TaskTracker::new(),
shutdown,
}
}
pub fn spawn<F, Fut>(&self, name: &'static str, task: F)
where
F: FnOnce(Shutdown) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
let future = task(self.shutdown.clone());
tracing::debug!(task = name, "background task started");
self.tracker.spawn(async move {
future.await;
tracing::debug!(task = name, "background task finished");
});
}
pub fn len(&self) -> usize {
self.tracker.len()
}
pub fn is_empty(&self) -> bool {
self.tracker.is_empty()
}
pub async fn shutdown(&self, timeout: Duration) -> bool {
self.shutdown.trigger();
self.tracker.close();
match tokio::time::timeout(timeout, self.tracker.wait()).await {
Ok(()) => true,
Err(_) => {
tracing::warn!(
remaining = self.tracker.len(),
"background tasks did not finish before the shutdown timeout"
);
false
}
}
}
}