use std::sync::atomic::Ordering;
use tokio::time::Duration;
use tracing::{debug, error, info, warn};
use super::ZerobusStream;
use crate::ZerobusResult;
const SHUTDOWN_TIMEOUT_SECS: u64 = 1;
impl ZerobusStream {
pub fn is_closed(&self) -> bool {
self.is_closed.load(Ordering::Relaxed)
}
pub async fn close(&mut self) -> ZerobusResult<()> {
if self.is_closed.load(Ordering::Relaxed) {
return Ok(());
}
if let Some(stream_id) = self.stream_id.as_deref() {
info!(stream_id = %stream_id, "Closing stream");
} else {
error!("Stream ID is None during closing");
}
let flush_result = self.flush().await;
self.is_closed.store(true, Ordering::Relaxed);
self.shutdown_all_tasks_gracefully().await;
flush_result
}
async fn shutdown_all_tasks_gracefully(&mut self) {
self.cancellation_token.cancel();
match tokio::time::timeout(
Duration::from_secs(SHUTDOWN_TIMEOUT_SECS),
&mut self.supervisor_task,
)
.await
{
Ok(_) => {
debug!("Supervisor task exited gracefully");
}
Err(_) => {
warn!("Supervisor task did not exit within timeout, aborting");
self.supervisor_task.abort();
}
}
if let Some(task) = self.callback_handler_task.take() {
Self::shutdown_callback_task(task, self.options.callback_max_wait_time_ms).await;
}
}
pub(super) async fn shutdown_callback_task(
mut task: tokio::task::JoinHandle<()>,
callback_max_wait_time_ms: Option<u64>,
) {
if let Some(callback_max_wait_time_ms) = callback_max_wait_time_ms {
match tokio::time::timeout(Duration::from_millis(callback_max_wait_time_ms), &mut task)
.await
{
Ok(_) => {
debug!("Callback handler task exited gracefully");
}
Err(_) => {
debug!("Callback handler task did not exit within timeout, aborting");
task.abort();
}
}
} else {
debug!("Callback max wait time is not set, waiting indefinitely");
let _ = (&mut task).await;
}
}
#[cfg(feature = "testing")]
pub(crate) fn signal_shutdown(&self) {
self.is_closed.store(true, Ordering::Relaxed);
self.cancellation_token.cancel();
}
}