waitfor/
lib.rs

1// lib.rs
2
3// Copyright (C) 2019 Daniel Mueller <deso@posteo.net>
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6//! A crate allowing for repeated retry of a function until it either
7//! reports success, fails with an error, or times out.
8
9use std::thread::sleep;
10use std::time::Duration;
11use std::time::Instant;
12
13
14/// Wait until an operation yields a result or a deadline is reached,
15/// polling every `interval`.
16/// On success the function returns `Ok(Some(T))`. When `deadline` is
17/// reached without a result being produced the return value is
18/// `Ok(None)`. Errors reported by the operation in question result in
19/// early return with `Err(E)`.
20pub fn wait_until<F, T, E>(deadline: Instant, interval: Duration, mut op: F) -> Result<Option<T>, E>
21where
22  F: FnMut() -> Result<Option<T>, E>,
23{
24  'l: loop {
25    match op() {
26      // No value means we just repeat.
27      Ok(None) => sleep(interval),
28      // We retrieved our value and are done.
29      v @ Ok(Some(_)) => break 'l v,
30      // Errors terminate our wait early.
31      e @ Err(_) => break 'l e,
32    }
33
34    if Instant::now() >= deadline {
35      break 'l Ok(None);
36    }
37  }
38}
39
40/// Wait until an operation yields a result or a timeout is reached,
41/// polling every `interval`.
42/// On success the function returns `Ok(Some(T))`. When `timeout` is
43/// exceeded without a result being produced the return value is
44/// `Ok(None)`. Errors reported by the operation in question result in
45/// early return with `Err(E)`.
46pub fn wait_for<F, T, E>(timeout: Duration, interval: Duration, op: F) -> Result<Option<T>, E>
47where
48  F: FnMut() -> Result<Option<T>, E>,
49{
50  wait_until(Instant::now() + timeout, interval, op)
51}
52
53
54#[cfg(test)]
55mod tests {
56  use super::*;
57
58  #[test]
59  fn wait_for_success() {
60    let result = wait_for::<_, _, ()>(Duration::from_secs(5), Duration::from_nanos(1), || {
61      static mut COUNTER: u64 = 1;
62      if unsafe { COUNTER } == 5 {
63        Ok(Some(5))
64      } else {
65        unsafe { COUNTER += 1 };
66        Ok(None)
67      }
68    });
69
70    assert_eq!(result, Ok(Some(5)))
71  }
72
73  #[test]
74  fn wait_for_error() {
75    let result = wait_for::<_, (), _>(Duration::from_secs(5), Duration::from_nanos(1), || {
76      static mut COUNTER: u64 = 1;
77      if unsafe { COUNTER } == 5 {
78        Err("expected")
79      } else {
80        unsafe { COUNTER += 1 };
81        Ok(None)
82      }
83    });
84
85    assert_eq!(result, Err("expected"))
86  }
87
88  #[test]
89  fn wait_for_timeout() {
90    let result = wait_for::<_, (), ()>(Duration::from_millis(10), Duration::from_nanos(1), || {
91      Ok(None)
92    });
93
94    assert_eq!(result, Ok(None))
95  }
96}