Skip to main content

coven_foundation/
clock_polling.rs

1use super::{Clock, FixedClock, WallTimerError};
2use std::future::Future;
3use std::pin::Pin;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::{Arc, Mutex};
6use std::time::{Duration, SystemTime};
7
8/// A fixed clock whose waits end only as its time reaches them. A backoff or
9/// cooldown the code waits out passes as the code arms it: this clock moves
10/// the fixed clock to the wait's end, as the wait's passing would, so retries
11/// run without real time and their timestamps are the waits the code chose.
12/// A deadline, which work races, and the idle interval end only when the test
13/// moves the clock, as does every wait once `hold_waits` is set. It records
14/// each `wait_out` it is given.
15pub struct PollingClock {
16    clock: Arc<FixedClock>,
17    /// Once set, waits end only when the test moves the fixed clock past
18    /// them, so what a failure reports stays until the test lets its retry
19    /// run.
20    held: AtomicBool,
21    waited: Mutex<Vec<Duration>>,
22}
23
24impl PollingClock {
25    /// A polling clock over `clock`, which it moves.
26    pub fn new(clock: Arc<FixedClock>) -> Self {
27        Self { clock, held: AtomicBool::new(false), waited: Mutex::new(Vec::new()) }
28    }
29    /// End later waits by the test's moves of the fixed clock alone; a wait
30    /// armed before ends as its own move reaches it.
31    pub fn hold_waits(&self) {
32        self.held.store(true, Ordering::SeqCst);
33    }
34    /// Pass later waits as the code arms them again; a wait armed while
35    /// they were held still ends only when the fixed clock reaches it.
36    pub fn release_waits(&self) {
37        self.held.store(false, Ordering::SeqCst);
38    }
39    /// Whether waits are held.
40    pub fn holds_waits(&self) -> bool {
41        self.held.load(Ordering::SeqCst)
42    }
43    /// Each backoff, cooldown or spacing the code armed, in order.
44    pub fn waited(&self) -> Vec<Duration> {
45        crate::locks::lock_unpoisoned(&self.waited).clone()
46    }
47}
48
49impl Clock for PollingClock {
50    fn now(&self) -> SystemTime {
51        self.clock.now()
52    }
53    fn elapsed(&self) -> Duration {
54        self.clock.elapsed()
55    }
56    fn deadline(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send>> {
57        self.clock.deadline(duration)
58    }
59    fn wait_out(&self, wait: Duration) -> Pin<Box<dyn Future<Output = ()> + Send>> {
60        crate::locks::lock_unpoisoned(&self.waited).push(wait);
61        let woken = self.clock.wait_out(wait);
62        if self.held.load(Ordering::SeqCst) {
63            return woken;
64        }
65        let end = self.clock.elapsed() + wait;
66        let clock = self.clock.clone();
67        Box::pin(async move {
68            let left = end.saturating_sub(clock.elapsed());
69            if !left.is_zero() {
70                clock.set(clock.now() + left);
71            }
72            woken.await;
73        })
74    }
75    fn sleep_until(
76        &self,
77        at: SystemTime,
78    ) -> Pin<Box<dyn Future<Output = Result<(), WallTimerError>> + Send>> {
79        self.clock.sleep_until(at)
80    }
81}