micro_tower/
shutdown.rs

1//! Utilities to manage unified shutdown on system events (e.g. SIGTERM, SIGQUIT)
2
3use tokio::signal::unix;
4use tokio::signal::unix::SignalKind;
5use tokio::task::JoinHandle;
6use tokio_util::sync::{CancellationToken, WaitForCancellationFuture};
7
8#[derive(Default)]
9pub struct Controller {
10    token: CancellationToken,
11}
12
13impl Clone for Controller {
14    fn clone(&self) -> Self {
15        Self {
16            token: self.token.child_token(),
17        }
18    }
19}
20
21impl Controller {
22    /// Create a new controller. Same as [`Controller::default`].
23    #[must_use]
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    /// Emit shutdown signal.
29    pub fn shutdown(&self) {
30        self.token.cancel();
31    }
32
33    /// Returns future to await shutdown signal.
34    pub fn wait_for_shutdown(&self) -> WaitForCancellationFuture<'_> {
35        self.token.cancelled()
36    }
37
38    /// Spawns a new handler which waits for shutdown signals.
39    ///
40    /// # Errors
41    ///
42    /// Will return `Err` if process wasn't able to acquire quit and terminate signal handler.
43    pub fn spawn_handler(self) -> std::io::Result<JoinHandle<()>> {
44        let mut qt = unix::signal(SignalKind::quit())?;
45        let mut tm = unix::signal(SignalKind::terminate())?;
46
47        let handle = tokio::spawn(async move {
48            tokio::select! {
49                _ = qt.recv() => {
50                    tracing::debug!("received SIGQUIT signal");
51                },
52                _ = tm.recv() => {
53                    tracing::debug!("received SIGTERM signal");
54                }
55                res = tokio::signal::ctrl_c() => {
56                    match res {
57                        Ok(_) => tracing::debug!("received ctrl-c shutdown request"),
58                        Err(err) => {
59                            let report = crate::report!(err);
60                            tracing::error!("{report:?}");
61                        }
62                    }
63                }
64            }
65            tracing::trace!("sending shutdown signal");
66            self.shutdown();
67            tracing::trace!("shutdown complete");
68        });
69        Ok(handle)
70    }
71}