jupiter_rs/
flag.rs

1use std::sync::atomic::{AtomicBool, Ordering};
2
3pub struct FlagListener {
4    rx: tokio::sync::broadcast::Receiver<bool>
5}
6
7impl FlagListener {
8    pub async fn expect(&mut self) -> Option<bool> {
9        self.rx.recv().await.ok()
10    }
11}
12
13pub struct Flag {
14    state: AtomicBool,
15    tx: tokio::sync::broadcast::Sender<bool>
16}
17
18impl Flag {
19    pub fn new(initial_value: bool) -> Self {
20        let (tx, _) = tokio::sync::broadcast::channel(1);
21        Flag { state: AtomicBool::new(initial_value), tx }
22    }
23
24    pub fn listener(&self) -> FlagListener {
25        FlagListener { rx: self.tx.subscribe() }
26    }
27
28    pub fn change(&self, new_value: bool) {
29        self.state.store(new_value, Ordering::SeqCst);
30        let _ = self.tx.send(new_value);
31    }
32
33    pub fn read(&self) -> bool {
34        self.state.load(Ordering::SeqCst)
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use crate::flag::Flag;
41
42    #[test]
43    fn toggling_works() {
44        let flag = Flag::new(true);
45        assert_eq!(flag.read(), true);
46        flag.change(false);
47        assert_eq!(flag.read(), false);
48    }
49
50    #[test]
51    fn toggling_works_across_threads() {
52        tokio_test::block_on(async move {
53            let outer_flag = Flag::new(true);
54            let mut outer_listener = outer_flag.listener();
55
56            let inner_flag = Flag::new(true);
57            let mut inner_listener = inner_flag.listener();
58
59            tokio::spawn(async move {
60                outer_listener.expect().await;
61                inner_flag.change(false);
62                assert_eq!(inner_flag.read(), false);
63            });
64
65            outer_flag.change(false);
66            inner_listener.expect().await;
67            assert_eq!(outer_flag.read(), false);
68        });
69    }
70}