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}