Skip to main content

futu_opend/startup/phase4/
shutdown.rs

1use std::time::Duration;
2
3pub(super) const SHUTDOWN_GRACE_PERIOD: Duration = Duration::from_secs(5);
4
5pub(super) type SurfaceJoinHandle = tokio::task::JoinHandle<anyhow::Result<()>>;
6pub(super) type BackgroundJoinHandle = tokio::task::JoinHandle<()>;
7
8pub(super) fn request_surface_shutdown(
9    shutdown_tx: &tokio::sync::watch::Sender<bool>,
10    reason: &'static str,
11) {
12    if let Err(error) = shutdown_tx.send(true) {
13        tracing::debug!(
14            reason,
15            error = %error,
16            "surface shutdown signal skipped; receivers already dropped"
17        );
18    }
19}
20
21async fn surface_task_result(handle: &mut SurfaceJoinHandle) -> anyhow::Result<()> {
22    match handle.await {
23        Ok(result) => result,
24        Err(error) => Err(anyhow::anyhow!("surface task join failed: {error}")),
25    }
26}
27
28pub(super) async fn surface_task_result_or_pending(
29    handle: &mut Option<SurfaceJoinHandle>,
30) -> anyhow::Result<()> {
31    match handle.as_mut() {
32        Some(handle) => surface_task_result(handle).await,
33        None => std::future::pending().await,
34    }
35}
36
37pub(super) fn log_surface_task_result(
38    surface: &'static str,
39    result: anyhow::Result<()>,
40) -> anyhow::Result<()> {
41    match result {
42        Ok(()) => {
43            tracing::warn!(surface, "surface task exited before daemon shutdown");
44            Ok(())
45        }
46        Err(error) => {
47            tracing::error!(surface, error = %error, "surface task failed");
48            Err(anyhow::anyhow!("{surface} surface task failed: {error}"))
49        }
50    }
51}
52
53pub(super) async fn await_surface_shutdown(
54    surface: &'static str,
55    mut handle: SurfaceJoinHandle,
56    grace_period: Duration,
57) {
58    match tokio::time::timeout(grace_period, &mut handle).await {
59        Ok(Ok(Ok(()))) => tracing::info!(surface, "surface task stopped gracefully"),
60        Ok(Ok(Err(error))) => {
61            tracing::error!(surface, error = %error, "surface task failed during shutdown")
62        }
63        Ok(Err(error)) => {
64            tracing::warn!(surface, error = %error, "surface task join failed during shutdown")
65        }
66        Err(_) => {
67            tracing::warn!(
68                surface,
69                grace_secs = grace_period.as_secs(),
70                "surface task did not stop in grace period; aborting fallback"
71            );
72            handle.abort();
73        }
74    }
75}
76
77pub(super) async fn await_background_shutdown(
78    task: &'static str,
79    mut handle: BackgroundJoinHandle,
80    grace_period: Duration,
81) {
82    match tokio::time::timeout(grace_period, &mut handle).await {
83        Ok(Ok(())) => tracing::info!(task, "background task stopped gracefully"),
84        Ok(Err(error)) => {
85            tracing::warn!(task, error = %error, "background task join failed during shutdown")
86        }
87        Err(_) => {
88            tracing::warn!(
89                task,
90                grace_secs = grace_period.as_secs(),
91                "background task did not stop in grace period; aborting fallback"
92            );
93            handle.abort();
94        }
95    }
96}