1use std::{
2 collections::VecDeque,
3 sync::{
4 Mutex,
5 atomic::{AtomicU64, Ordering},
6 },
7};
8
9use sim_kernel::{Error, Result};
10
11use crate::ServerFrame;
12
13#[derive(Default)]
15pub struct FrameRouter {
16 next_id: AtomicU64,
17 inbound: Mutex<VecDeque<ServerFrame>>,
18}
19
20impl FrameRouter {
21 pub fn fresh_msg_id(&self) -> u64 {
23 self.next_id.fetch_add(1, Ordering::Relaxed) + 1
24 }
25
26 pub fn peek_next_msg_id(&self) -> u64 {
29 self.next_id.load(Ordering::Relaxed) + 1
30 }
31
32 pub fn push_inbound(&self, frame: ServerFrame) -> Result<()> {
37 self.inbound
38 .lock()
39 .map_err(|_| Error::PoisonedLock("frame router inbound queue"))?
40 .push_back(frame);
41 Ok(())
42 }
43
44 pub fn pop_inbound(&self) -> Result<Option<ServerFrame>> {
49 Ok(self
50 .inbound
51 .lock()
52 .map_err(|_| Error::PoisonedLock("frame router inbound queue"))?
53 .pop_front())
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use std::{sync::Arc, thread};
60
61 use sim_kernel::{Error, Symbol};
62
63 use super::FrameRouter;
64 use crate::{FrameEnvelope, FrameKind, ServerFrame};
65
66 fn poison(router: &Arc<FrameRouter>) {
67 let poisoner = Arc::clone(router);
68 let _ = thread::spawn(move || {
69 let _guard = poisoner.inbound.lock().unwrap();
70 panic!("intentionally poison the inbound queue mutex");
71 })
72 .join();
73 }
74
75 fn frame() -> ServerFrame {
76 ServerFrame::new(
77 Symbol::new("lisp"),
78 FrameKind::Notify,
79 FrameEnvelope::default(),
80 Vec::new(),
81 )
82 }
83
84 #[test]
85 fn push_and_pop_return_poisoned_lock_instead_of_panicking() {
86 let router = Arc::new(FrameRouter::default());
87 poison(&router);
88
89 assert!(matches!(
90 router.push_inbound(frame()),
91 Err(Error::PoisonedLock("frame router inbound queue"))
92 ));
93 assert!(matches!(
94 router.pop_inbound(),
95 Err(Error::PoisonedLock("frame router inbound queue"))
96 ));
97 }
98}