use std::time::Duration;
use tokio_util::sync::CancellationToken;
use tokio_util::task::TaskTracker;
use super::super::RequestHandler;
#[derive(Debug, Default, Clone)]
pub struct InFlight {
shutdown: CancellationToken,
executors: TaskTracker,
background: TaskTracker,
}
impl InFlight {
pub(crate) fn task_token(&self) -> CancellationToken {
self.shutdown.child_token()
}
pub(crate) fn shutdown_token(&self) -> CancellationToken {
self.shutdown.clone()
}
pub(crate) const fn executors(&self) -> &TaskTracker {
&self.executors
}
pub(crate) const fn background(&self) -> &TaskTracker {
&self.background
}
pub(crate) fn cancel_all(&self) {
self.shutdown.cancel();
}
pub(crate) async fn wait(&self) {
self.executors.close();
self.background.close();
self.executors.wait().await;
self.background.wait().await;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct InFlightReport {
pub completed: usize,
pub cancelled: usize,
pub still_running: usize,
pub finished: bool,
}
impl RequestHandler {
pub async fn cancel_in_flight(&self, grace: Duration) -> InFlightReport {
let cancelled = self.in_flight.executors().len();
self.in_flight.cancel_all();
trace_info!(
executors = cancelled,
grace_ms = u64::try_from(grace.as_millis()).unwrap_or(u64::MAX),
"shutdown: cancelling in-flight tasks"
);
let finished = tokio::time::timeout(grace, self.in_flight.wait())
.await
.is_ok();
let still_running = self.in_flight.executors().len();
if !finished {
trace_warn!(
still_running,
"shutdown: grace period ended with work still running"
);
}
InFlightReport {
completed: 0,
cancelled,
still_running,
finished,
}
}
pub async fn finish_in_flight(&self, completion: Duration, grace: Duration) -> InFlightReport {
let running = self.in_flight.executors().len();
trace_info!(
executors = running,
completion_ms = u64::try_from(completion.as_millis()).unwrap_or(u64::MAX),
"shutdown: letting in-flight tasks finish"
);
let _ = tokio::time::timeout(completion, self.in_flight.wait()).await;
let completed = running.saturating_sub(self.in_flight.executors().len());
InFlightReport {
completed,
..self.cancel_in_flight(grace).await
}
}
}