about summary refs log tree commit diff
path: root/tower-watchdog/src/watchdog.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tower-watchdog/src/watchdog.rs')
-rw-r--r--tower-watchdog/src/watchdog.rs45
1 files changed, 45 insertions, 0 deletions
diff --git a/tower-watchdog/src/watchdog.rs b/tower-watchdog/src/watchdog.rs
new file mode 100644
index 0000000..4162294
--- /dev/null
+++ b/tower-watchdog/src/watchdog.rs
@@ -0,0 +1,45 @@
+/// A watchdog timer with a timeout that needs to be regularly pinged
+/// to prevent it from firing.
+///
+/// **Note**: the behavior of the watchdog is undefined (but will not
+/// result in a crash or put the watchdog in an invalid state) if it
+/// is pet at the last possible moment, for example, like this:
+///
+/// ```no_compile
+/// let (watchdog, pet) = /* ... */
+/// tokio::select! {
+///     tokio::time::sleep(watchdog.timeout).then(move |()| pet.pet()) = _ => { eprintln!("foo"); },
+///     watchdog.wait() = _ => { eprintln!("bar"); }
+/// ```
+pub struct Watchdog {
+    rx: tokio::sync::mpsc::Receiver<()>,
+    /// Timeout of this watchdog.
+    pub timeout: std::time::Duration,
+}
+#[derive(Clone)]
+pub(crate) struct Pet(tokio::sync::mpsc::Sender<()>);
+
+pub(crate) fn watchdog(timeout: std::time::Duration) -> (Watchdog, Pet) {
+    let (tx, rx) = tokio::sync::mpsc::channel::<()>(1);
+    let pet = Pet(tx);
+    let watchdog = Watchdog { rx, timeout };
+
+    (watchdog, pet)
+}
+
+impl Watchdog {
+    /// Wait until the watchdog fires from not being pet in a while.
+    ///
+    /// The watchdog doesn't start waiting for pets and scritches
+    /// until you await the future returned from this method.
+    pub async fn wait(mut self) {
+        while let Ok(Some(())) = tokio::time::timeout(self.timeout, self.rx.recv()).await {}
+    }
+}
+
+impl Pet {
+    pub(crate) async fn pet_owned(self) -> Result<(), tokio::sync::mpsc::error::SendError<()>> {
+        let tx = self.0.clone();
+        tx.send(()).await
+    }
+}