use std::time::Duration;
#[cfg(test)]
use std::time::Instant;
use super::RequestHandler;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[must_use = "a shutdown that was not graceful is worth reporting; \
call .is_graceful() or log the report"]
pub struct ShutdownReport {
pub queues_force_destroyed: usize,
pub executor_cleanup_completed: bool,
}
impl ShutdownReport {
#[must_use]
pub const fn is_graceful(self) -> bool {
self.queues_force_destroyed == 0 && self.executor_cleanup_completed
}
}
impl RequestHandler {
pub async fn shutdown(&self) -> ShutdownReport {
{
let tokens = self.cancellation_tokens.read().await;
for entry in tokens.values() {
entry.token.cancel();
}
}
self.event_queue_manager.destroy_all().await;
{
let mut tokens = self.cancellation_tokens.write().await;
tokens.clear();
}
let executor_cleanup_completed =
tokio::time::timeout(Duration::from_secs(10), self.executor.on_shutdown())
.await
.is_ok();
if !executor_cleanup_completed {
trace_warn!("executor cleanup did not finish within the shutdown timeout");
}
ShutdownReport {
queues_force_destroyed: 0,
executor_cleanup_completed,
}
}
pub async fn shutdown_with_timeout(&self, timeout: Duration) -> ShutdownReport {
{
let tokens = self.cancellation_tokens.read().await;
for entry in tokens.values() {
entry.token.cancel();
}
}
let drain_deadline = tokio::time::Instant::now() + timeout;
let mut queues_force_destroyed = 0;
loop {
let active = self.event_queue_manager.active_count().await;
if active == 0 {
break;
}
if tokio::time::Instant::now() >= drain_deadline {
trace_warn!(
active_queues = active,
"shutdown timeout reached, force-destroying remaining queues"
);
queues_force_destroyed = active;
break;
}
let remaining = drain_deadline - tokio::time::Instant::now();
tokio::time::sleep(remaining.min(tokio::time::Duration::from_millis(10))).await;
}
self.event_queue_manager.destroy_all().await;
{
let mut tokens = self.cancellation_tokens.write().await;
tokens.clear();
}
let executor_cleanup_completed = tokio::time::timeout(timeout, self.executor.on_shutdown())
.await
.is_ok();
if !executor_cleanup_completed {
trace_warn!("executor cleanup did not finish within the shutdown timeout");
}
ShutdownReport {
queues_force_destroyed,
executor_cleanup_completed,
}
}
}
#[cfg(test)]
mod tests;