Skip to main content

epics_seq/
event_flag.rs

1use std::sync::atomic::{AtomicBool, Ordering};
2use std::sync::Arc;
3use tokio::sync::Notify;
4
5/// Event flag set: logical flags + wakeup mechanism for state sets.
6///
7/// Separates logical state (AtomicBool per flag) from wakeup delivery
8/// (Notify per state set). Setting a flag wakes all state sets.
9pub struct EventFlagSet {
10    flags: Vec<AtomicBool>,
11    /// flag_id → list of synced channel ids.
12    sync_map: Vec<Vec<usize>>,
13    /// One Notify per state set for wakeup.
14    ss_wakeups: Vec<Arc<Notify>>,
15}
16
17impl EventFlagSet {
18    pub fn new(
19        num_flags: usize,
20        sync_map: Vec<Vec<usize>>,
21        ss_wakeups: Vec<Arc<Notify>>,
22    ) -> Self {
23        let flags = (0..num_flags).map(|_| AtomicBool::new(false)).collect();
24        Self {
25            flags,
26            sync_map,
27            ss_wakeups,
28        }
29    }
30
31    /// Set a flag and wake all state sets.
32    pub fn set(&self, ef_id: usize) {
33        if let Some(flag) = self.flags.get(ef_id) {
34            flag.store(true, Ordering::Release);
35            for notify in &self.ss_wakeups {
36                notify.notify_one();
37            }
38        }
39    }
40
41    /// Test a flag (non-destructive).
42    pub fn test(&self, ef_id: usize) -> bool {
43        self.flags
44            .get(ef_id)
45            .map_or(false, |f| f.load(Ordering::Acquire))
46    }
47
48    /// Clear a flag. Returns the previous value.
49    pub fn clear(&self, ef_id: usize) -> bool {
50        self.flags
51            .get(ef_id)
52            .map_or(false, |f| f.swap(false, Ordering::AcqRel))
53    }
54
55    /// Test and clear atomically. Returns the previous value.
56    pub fn test_and_clear(&self, ef_id: usize) -> bool {
57        self.flags
58            .get(ef_id)
59            .map_or(false, |f| f.swap(false, Ordering::AcqRel))
60    }
61
62    /// Get the channel ids synced to a given event flag.
63    pub fn synced_channels(&self, ef_id: usize) -> &[usize] {
64        self.sync_map.get(ef_id).map_or(&[], |v| v.as_slice())
65    }
66
67    pub fn num_flags(&self) -> usize {
68        self.flags.len()
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    fn make_efs(num_flags: usize, sync_map: Vec<Vec<usize>>, num_ss: usize) -> EventFlagSet {
77        let wakeups = (0..num_ss).map(|_| Arc::new(Notify::new())).collect();
78        EventFlagSet::new(num_flags, sync_map, wakeups)
79    }
80
81    #[test]
82    fn test_set_and_test() {
83        let efs = make_efs(3, vec![vec![]; 3], 1);
84        assert!(!efs.test(0));
85        efs.set(0);
86        assert!(efs.test(0));
87        assert!(!efs.test(1));
88    }
89
90    #[test]
91    fn test_clear() {
92        let efs = make_efs(2, vec![vec![]; 2], 1);
93        efs.set(0);
94        assert!(efs.clear(0));
95        assert!(!efs.test(0));
96        // clear again returns false
97        assert!(!efs.clear(0));
98    }
99
100    #[test]
101    fn test_test_and_clear() {
102        let efs = make_efs(2, vec![vec![]; 2], 1);
103        efs.set(1);
104        assert!(efs.test_and_clear(1));
105        assert!(!efs.test(1));
106        assert!(!efs.test_and_clear(1));
107    }
108
109    #[test]
110    fn test_synced_channels() {
111        let efs = make_efs(2, vec![vec![0, 1], vec![2]], 1);
112        assert_eq!(efs.synced_channels(0), &[0, 1]);
113        assert_eq!(efs.synced_channels(1), &[2]);
114    }
115
116    #[test]
117    fn test_invalid_flag_id() {
118        let efs = make_efs(1, vec![vec![]], 1);
119        assert!(!efs.test(99));
120        assert!(!efs.clear(99));
121        assert!(!efs.test_and_clear(99));
122        assert_eq!(efs.synced_channels(99), &[] as &[usize]);
123    }
124}