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}