Skip to main content

signal_mod/
token.rs

1//! Cloneable observer and initiator handles.
2
3use std::sync::Arc;
4use std::time::Duration;
5
6use crate::reason::ShutdownReason;
7use crate::state::Inner;
8
9/// Cloneable observer handle.
10///
11/// Hand one of these to every subsystem that needs to react to
12/// shutdown. The handle is cheap to clone (single `Arc::clone`).
13#[derive(Debug, Clone)]
14pub struct ShutdownToken {
15    inner: Arc<Inner>,
16}
17
18impl ShutdownToken {
19    pub(crate) fn new(inner: Arc<Inner>) -> Self {
20        Self { inner }
21    }
22
23    /// `true` if shutdown has been initiated.
24    #[must_use]
25    pub fn is_initiated(&self) -> bool {
26        self.inner.is_initiated()
27    }
28
29    /// Reason for the shutdown, if one has been initiated.
30    #[must_use]
31    pub fn reason(&self) -> Option<ShutdownReason> {
32        self.inner.reason()
33    }
34
35    /// Wall-clock time since shutdown was initiated.
36    #[must_use]
37    pub fn elapsed(&self) -> Option<Duration> {
38        self.inner.elapsed()
39    }
40
41    /// Block the current thread until shutdown is initiated.
42    ///
43    /// Returns immediately if shutdown is already initiated.
44    pub fn wait_blocking(&self) {
45        self.inner.wait_blocking();
46    }
47
48    /// Block the current thread for at most `timeout`.
49    ///
50    /// Returns `true` if shutdown was observed within the budget,
51    /// `false` if the timeout elapsed first.
52    pub fn wait_blocking_timeout(&self, timeout: Duration) -> bool {
53        self.inner.wait_blocking_timeout(timeout)
54    }
55
56    /// Async wait point. Returns once shutdown is initiated.
57    ///
58    /// Requires the `tokio` feature; the `async-std` variant takes
59    /// precedence only when `tokio` is not enabled.
60    #[cfg(feature = "tokio")]
61    #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
62    pub async fn wait(&self) {
63        if self.inner.is_initiated() {
64            return;
65        }
66        let mut rx = self.inner.tx.subscribe();
67        if self.inner.is_initiated() {
68            return;
69        }
70        let _ = rx.recv().await;
71    }
72
73    /// Async wait point. Returns once shutdown is initiated.
74    ///
75    /// Compiled only with the `async-std` feature and when `tokio`
76    /// is not enabled.
77    #[cfg(all(feature = "async-std", not(feature = "tokio")))]
78    #[cfg_attr(docsrs, doc(cfg(feature = "async-std")))]
79    pub async fn wait(&self) {
80        let mut poll = Duration::from_millis(1);
81        let cap = Duration::from_millis(50);
82        while !self.inner.is_initiated() {
83            async_std::task::sleep(poll).await;
84            poll = (poll * 2).min(cap);
85        }
86    }
87}
88
89/// Cloneable initiator handle.
90///
91/// Hand one of these to any code path that may need to ask for
92/// shutdown.
93#[derive(Debug, Clone)]
94pub struct ShutdownTrigger {
95    inner: Arc<Inner>,
96}
97
98impl ShutdownTrigger {
99    pub(crate) fn new(inner: Arc<Inner>) -> Self {
100        Self { inner }
101    }
102
103    /// Initiate shutdown with the given reason.
104    ///
105    /// Returns `true` if this call performed the transition;
106    /// `false` if it was already initiated.
107    pub fn trigger(&self, reason: ShutdownReason) -> bool {
108        self.inner.trigger(reason)
109    }
110
111    /// `true` if shutdown has been initiated.
112    #[must_use]
113    pub fn is_initiated(&self) -> bool {
114        self.inner.is_initiated()
115    }
116}