Skip to main content

concinnity_core/ecs/
event.rs

1// Double-buffered event queue with per-reader cursors. Replaces the
2// drained-Vec-as-event pattern, which is lossy within a frame (one reader's
3// drain hides the events from every other reader, and multiple sends collapse
4// to last-write-wins). Here an event stays readable for two `update` cycles, so
5// any reader that runs after the writer sees it, and every reader sees every
6// event exactly once.
7//
8// Each event carries a monotonically increasing sequence id. A reader's cursor
9// stores the next id it has not yet seen; reading yields every buffered event
10// at or past the cursor and advances it.
11
12use alloc::vec::Vec;
13
14/// A double-buffered event queue: events stay readable for two frames.
15pub struct Events<E> {
16    // Two frame buffers. `newest` indexes the one new events go into; the other
17    // holds the previous frame's events, still readable.
18    buffers: [Vec<E>; 2],
19    newest: usize,
20    // Id assigned to the next event sent.
21    next_id: usize,
22    // Sequence id of the first event in each buffer.
23    starts: [usize; 2],
24}
25
26#[derive(Clone, Copy, Debug, Default)]
27/// A reader's position in an [`Events`] queue.
28pub struct EventCursor {
29    // Next sequence id this reader has not yet consumed.
30    next: usize,
31}
32
33impl<E> Default for Events<E> {
34    fn default() -> Events<E> {
35        Events {
36            buffers: [Vec::new(), Vec::new()],
37            newest: 0,
38            next_id: 0,
39            starts: [0, 0],
40        }
41    }
42}
43
44impl<E> Events<E> {
45    /// An empty queue.
46    pub fn new() -> Events<E> {
47        Events::default()
48    }
49
50    /// Queue an event. It becomes visible to readers immediately and stays
51    /// readable until the second `update` after this one.
52    pub fn send(&mut self, event: E) {
53        self.buffers[self.newest].push(event);
54        self.next_id += 1;
55    }
56
57    /// Advance one frame: retire the older buffer and start a fresh newest one.
58    /// Events older than two cycles are dropped.
59    pub fn update(&mut self) {
60        let oldest = self.newest ^ 1;
61        self.buffers[oldest].clear();
62        self.starts[oldest] = self.next_id;
63        self.newest = oldest;
64    }
65
66    /// Read every buffered event the cursor has not yet seen, in send order, and
67    /// advance the cursor past them.
68    ///
69    /// Lazy: a drain costs no allocation, which matters because every event
70    /// reader does this every frame. The cursor advances here rather than as the
71    /// iterator is consumed, so a caller that reads only part of the run still
72    /// ends up past all of it -- the same thing a returned collection did, and
73    /// the only behaviour that makes "every reader sees every event exactly
74    /// once" hold for a partial read.
75    pub fn read(&self, cursor: &mut EventCursor) -> impl Iterator<Item = &E> {
76        // Visit buffers oldest-first so events come back in send order.
77        let (older, newer) = if self.starts[0] > self.starts[1] {
78            (1, 0)
79        } else {
80            (0, 1)
81        };
82        let from = cursor.next;
83        cursor.next = self.next_id;
84        self.unseen(older, from).chain(self.unseen(newer, from))
85    }
86
87    // The events in one buffer at or past sequence id `from`.
88    fn unseen(&self, buffer: usize, from: usize) -> impl Iterator<Item = &E> {
89        let start = self.starts[buffer];
90        // Ids within a buffer are contiguous from `start`, so the cut is a
91        // position rather than a per-event test.
92        let skip = from.saturating_sub(start);
93        self.buffers[buffer].iter().skip(skip)
94    }
95
96    /// Total events currently buffered across both frames.
97    pub fn len(&self) -> usize {
98        self.buffers[0].len() + self.buffers[1].len()
99    }
100
101    /// Whether both frame buffers are empty.
102    pub fn is_empty(&self) -> bool {
103        self.buffers[0].is_empty() && self.buffers[1].is_empty()
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use alloc::vec;
111
112    #[test]
113    fn reader_sees_each_event_once() {
114        let mut events: Events<u32> = Events::new();
115        events.send(1);
116        events.send(2);
117        let mut cursor = EventCursor::default();
118        let first: Vec<u32> = events.read(&mut cursor).copied().collect();
119        assert_eq!(first, vec![1, 2]);
120        // A second read with the same cursor sees nothing new.
121        assert_eq!(events.read(&mut cursor).count(), 0);
122        // A newly sent event is picked up.
123        events.send(3);
124        let next: Vec<u32> = events.read(&mut cursor).copied().collect();
125        assert_eq!(next, vec![3]);
126    }
127
128    #[test]
129    fn multiple_readers_each_see_all_events() {
130        let mut events: Events<u32> = Events::new();
131        events.send(10);
132        events.send(20);
133        let mut a = EventCursor::default();
134        let mut b = EventCursor::default();
135        let read_a: Vec<u32> = events.read(&mut a).copied().collect();
136        let read_b: Vec<u32> = events.read(&mut b).copied().collect();
137        assert_eq!(read_a, vec![10, 20]);
138        assert_eq!(read_b, vec![10, 20]);
139    }
140
141    #[test]
142    fn events_survive_one_update_then_drop() {
143        let mut events: Events<u32> = Events::new();
144        events.send(1);
145        events.update();
146        // Still readable one cycle later, in send order with a later event.
147        events.send(2);
148        let mut cursor = EventCursor::default();
149        let seen: Vec<u32> = events.read(&mut cursor).copied().collect();
150        assert_eq!(seen, vec![1, 2]);
151        // Two updates retire the first event entirely.
152        events.update();
153        events.update();
154        assert!(events.is_empty());
155    }
156}