Skip to main content

coven_sync/
sync_often.rs

1//! Apps asking for checks more often while a guard lives: the idle loop's
2//! passes (E5) and a waiting join's checks (ยง13.2).
3
4use std::{collections::BTreeMap, time::Duration};
5use tokio::sync::watch;
6
7/// How long an idle loop waits between passes while no app asks for less.
8pub(crate) const IDLE_INTERVAL: Duration = Duration::from_secs(30);
9
10/// The intervals apps hold, each with how many guards hold it.
11pub struct SyncOften(watch::Sender<BTreeMap<Duration, usize>>);
12
13impl SyncOften {
14    /// No interval held.
15    pub fn new() -> Self {
16        Self(watch::channel(BTreeMap::new()).0)
17    }
18    /// Hold idle passes at most `interval` apart until the guard drops.
19    pub fn hold(&self, interval: Duration) -> SyncOftenGuard {
20        self.0.send_modify(|held| *held.entry(interval).or_default() += 1);
21        SyncOftenGuard { held: self.0.clone(), interval }
22    }
23    /// The intervals held now, and their changes.
24    pub fn subscribe(&self) -> HeldIntervals {
25        HeldIntervals(self.0.subscribe())
26    }
27}
28
29/// What the guards hold, as the owner of a wait reads it.
30#[derive(Clone)]
31pub struct HeldIntervals(watch::Receiver<BTreeMap<Duration, usize>>);
32
33impl HeldIntervals {
34    /// The shortest interval held, if any guard lives.
35    pub fn shortest(&self) -> Option<Duration> {
36        self.0.borrow().keys().next().copied()
37    }
38    /// The wait between idle passes: the shortest interval held, never
39    /// longer than 30 seconds. The idle loop caps it; a join's checks do not.
40    pub(crate) fn idle_wait(&self) -> Duration {
41        self.shortest().map_or(IDLE_INTERVAL, |shortest| shortest.min(IDLE_INTERVAL))
42    }
43    /// Resolves when a guard is taken or dropped. Without the owner of the
44    /// holds no guard can be taken, and every guard keeps it, so the wait
45    /// then never ends.
46    pub async fn changed(&mut self) {
47        if self.0.changed().await.is_err() {
48            std::future::pending::<()>().await;
49        }
50    }
51}
52
53/// An app's request for idle passes at most its interval apart. The
54/// request lasts until the guard drops; the shortest interval held wins.
55#[derive(Debug)]
56#[must_use = "dropping the guard ends the request at once"]
57pub struct SyncOftenGuard {
58    held: watch::Sender<BTreeMap<Duration, usize>>,
59    interval: Duration,
60}
61
62impl Drop for SyncOftenGuard {
63    fn drop(&mut self) {
64        self.held.send_modify(|held| {
65            let count = held
66                .get_mut(&self.interval)
67                .expect("a guard's interval stays held until the guard drops");
68            *count -= 1;
69            if *count == 0 {
70                held.remove(&self.interval);
71            }
72        });
73    }
74}