1pub mod engine;
15pub mod event;
16pub mod handler;
17
18pub use engine::{EventEngine, PutError};
19pub use event::{Event, EventData};
20pub use handler::HandlerFn;
21pub use handler::HandlerId;
22pub use handler::HandlerRegistry;
23
24pub const EVENT_TIMER: &str = "eTimer";
26
27#[cfg(test)]
28mod tests {
29 use super::*;
30 use std::sync::atomic::{AtomicUsize, Ordering};
31 use std::sync::{Arc, Mutex};
32 use std::time::Duration;
33
34 fn wait_for(cond: impl Fn() -> bool) -> bool {
36 for _ in 0..200 {
37 if cond() {
38 return true;
39 }
40 std::thread::sleep(Duration::from_millis(10));
41 }
42 cond()
43 }
44
45 #[test]
47 fn dispatch_order_type_then_general() {
48 let engine = EventEngine::new();
49 engine.start();
50 let seq: Arc<spin_free_seq::Seq> = Arc::default();
51 let s1 = seq.clone();
52 engine.register("eTick.", Arc::new(move |_| s1.record(0)));
53 let s2 = seq.clone();
54 engine.register_general(Arc::new(move |_| s2.record(1)));
55 engine.put(Event::new("eTick.")).unwrap();
58 for _ in 0..50 {
59 if seq.is(0, 1) {
60 break;
61 }
62 std::thread::sleep(Duration::from_millis(10));
63 }
64 assert_eq!(seq.0.load(Ordering::SeqCst) & 3, 3);
67 engine.stop();
68 }
69
70 #[test]
72 fn timer_event_fires() {
73 let engine = EventEngine::with_interval(Duration::from_millis(50));
74 engine.start();
75 let cnt = Arc::new(AtomicUsize::new(0));
76 let c2 = cnt.clone();
77 engine.register(
78 EVENT_TIMER,
79 Arc::new(move |_| {
80 c2.fetch_add(1, Ordering::SeqCst);
81 }),
82 );
83 std::thread::sleep(Duration::from_millis(400));
84 let n = cnt.load(Ordering::SeqCst);
85 assert!(n >= 3, "timer fired too few times: {n}");
86 engine.stop();
87 }
88
89 #[test]
91 fn duplicate_registration_yields_unique_ids() {
92 let engine = EventEngine::new(); let bump = Arc::new(AtomicUsize::new(0));
94 let b1 = bump.clone();
95 let id1 = engine.register(
96 "eTick.",
97 Arc::new(move |_| {
98 b1.fetch_add(1, Ordering::SeqCst);
99 }),
100 );
101 let b2 = bump.clone();
102 let id2 = engine.register(
103 "eTick.",
104 Arc::new(move |_| {
105 b2.fetch_add(1, Ordering::SeqCst);
106 }),
107 );
108 assert_ne!(id1, id2, "each registration must get a unique HandlerId");
109 assert!(engine.unregister(id1));
110 assert!(engine.unregister(id2));
111 assert!(!engine.unregister(id1), "double unregister must be false");
112 }
113
114 #[test]
116 fn handler_panic_does_not_kill_dispatcher() {
117 let engine = EventEngine::new();
118 engine.start();
119 let cnt = Arc::new(AtomicUsize::new(0));
120 let _ = engine.register(
122 "eTick.",
123 Arc::new(move |_| {
124 panic!("boom");
125 }),
126 );
127 let c2 = cnt.clone();
128 engine.register(
129 "eTick.",
130 Arc::new(move |_| {
131 c2.fetch_add(1, Ordering::SeqCst);
132 }),
133 );
134 engine.put(Event::new("eTick.")).unwrap();
135 assert!(
136 wait_for(|| cnt.load(Ordering::SeqCst) >= 1),
137 "handler after a panicking one must still run"
138 );
139 engine.put(Event::new("eTick.")).unwrap();
140 assert!(
141 wait_for(|| cnt.load(Ordering::SeqCst) >= 2),
142 "events dispatched after a handler panic must still be processed"
143 );
144 engine.stop();
145 }
146
147 #[test]
149 fn put_after_stop_fails_and_stop_is_idempotent() {
150 let engine = EventEngine::new();
151 engine.start();
152 engine.stop();
153 assert!(!engine.is_active());
154 assert_eq!(engine.put(Event::new("eTick.")), Err(PutError::Stopped));
155 engine.stop(); engine.stop(); assert_eq!(engine.pending_events(), 0, "stop must reset pending count");
158 }
159
160 #[test]
162 fn same_type_events_delivered_in_put_order() {
163 let engine = EventEngine::new();
164 engine.start();
165 let seen: Arc<Mutex<Vec<usize>>> = Arc::default();
166 let s = seen.clone();
167 let _ = engine.register(
168 "eTick.",
169 Arc::new(move |e| {
170 let v = *e.data.downcast_ref::<usize>().expect("usize payload");
171 s.lock().unwrap().push(v);
172 }),
173 );
174 const N: usize = 200;
175 for i in 0..N {
176 engine.put(Event::with_data("eTick.", Arc::new(i))).unwrap();
177 }
178 assert!(
179 wait_for(|| seen.lock().unwrap().len() >= N),
180 "all events must be delivered"
181 );
182 let expected: Vec<usize> = (0..N).collect();
183 assert_eq!(
184 *seen.lock().unwrap(),
185 expected,
186 "delivery must preserve put order"
187 );
188 engine.stop();
189 }
190
191 #[test]
194 fn event_engine_is_sync_and_concurrent_put_works() {
195 fn assert_send_sync<T: Send + Sync>() {}
196 assert_send_sync::<EventEngine>();
197
198 let engine = Arc::new(EventEngine::new());
199 engine.start(); let cnt = Arc::new(AtomicUsize::new(0));
201 let c = cnt.clone();
202 engine.register(
203 "eTick.",
204 Arc::new(move |_| {
205 c.fetch_add(1, Ordering::SeqCst);
206 }),
207 );
208 let mut handles = Vec::new();
209 for _ in 0..4 {
210 let e = engine.clone();
211 handles.push(std::thread::spawn(move || {
212 for _ in 0..50 {
213 e.put(Event::new("eTick.")).unwrap();
214 }
215 }));
216 }
217 for h in handles {
218 h.join().unwrap();
219 }
220 const N: usize = 200;
221 assert!(
222 wait_for(|| cnt.load(Ordering::SeqCst) >= N),
223 "all cross-thread events must be delivered"
224 );
225 engine.stop();
226 }
227
228 mod spin_free_seq {
230 use std::sync::atomic::{AtomicI64, Ordering};
231 pub struct Seq(pub AtomicI64);
232 impl Seq {
233 pub fn record(&self, i: i64) {
234 let _ = self.0.fetch_or(1 << i, Ordering::SeqCst);
235 }
236 pub fn is(&self, a: i64, b: i64) -> bool {
237 self.0.load(Ordering::SeqCst) == (1 << a) + (1 << b)
238 }
239 }
240 impl Default for Seq {
241 fn default() -> Self {
242 Self(AtomicI64::new(0))
243 }
244 }
245 }
246}