gts_logger/logbackend/
threadmock.rs

1use crate::error::GtsLoggerError;
2use crate::logbackend::LogBackend;
3use core::fmt::Debug;
4use gts_transport::error::GtsTransportError;
5use gts_transport::membackend::memchunk::MemChunkHolder;
6use gts_transport::sync::lfringspsc::{spsc_ring_pair, SpScRingData, SpScRingSender};
7use std::cell::UnsafeCell;
8use std::sync::mpsc::Receiver;
9use std::sync::Mutex;
10use std::sync::{mpsc, Arc};
11use std::time::Duration;
12
13pub struct LogContext {}
14
15pub struct MockThreadLogBacked<const RSIZE: usize, T>
16where
17    T: Copy + Send,
18{
19    queue_rx: Receiver<T>,
20    run_flag: Arc<Mutex<bool>>,
21    join_handle: Option<std::thread::JoinHandle<()>>,
22    log_tx: UnsafeCell<SpScRingSender<RSIZE, T, MemChunkHolder<SpScRingData<RSIZE, T>>>>,
23}
24
25impl<T, const RSIZE: usize> MockThreadLogBacked<RSIZE, T>
26where
27    T: Copy + Send + 'static + Debug,
28{
29    pub fn new() -> Self {
30        let flag = Arc::new(Mutex::new(false));
31        // let queue = Arc::new(Mutex::new(VecDeque::<T>::new()));
32
33        let flag_clone = flag.clone();
34        // let queue_clone = queue.clone();
35        let (queue_tx, queue_rx) = mpsc::channel();
36        let (log_tx, mut log_rx) = spsc_ring_pair::<RSIZE, T, _>(MemChunkHolder::zeroed());
37
38        let join_handle = Some(std::thread::spawn(move || {
39            let queue_tx = queue_tx;
40            while !*flag_clone.lock().unwrap() {
41                match log_rx.try_recv() {
42                    Ok(res) => {
43                        queue_tx.send(*res).unwrap();
44                    }
45                    Err(GtsTransportError::WouldBlock) => {}
46                    _ => unreachable!(),
47                }
48                std::thread::sleep(Duration::from_millis(100));
49            }
50        }));
51
52        MockThreadLogBacked {
53            queue_rx,
54            run_flag: flag,
55            join_handle,
56            log_tx: log_tx.into(),
57        }
58    }
59
60    pub fn pop_front(&self) -> Option<T> {
61        self.queue_rx.try_recv().ok()
62    }
63}
64impl<T, const RSIZE: usize> Drop for MockThreadLogBacked<RSIZE, T>
65where
66    T: Copy + Send,
67{
68    fn drop(&mut self) {
69        *self.run_flag.lock().unwrap() = true;
70        self.join_handle.take().unwrap().join().unwrap();
71    }
72}
73
74impl<T, const RSIZE: usize> LogBackend<T> for MockThreadLogBacked<RSIZE, T>
75where
76    T: Copy + Send,
77{
78    fn log(&self, event: T) -> Result<(), GtsLoggerError> {
79        // SAFETY: Self is !Sync, only this function uses log_tx,
80        // no reentrancy in this function.
81        // but need verify reentrancy (by signal e.g.)
82        // anyway refcell doesn't check signal-reentrancy either.
83        let log_tx = unsafe { &mut *self.log_tx.get() };
84        log_tx.send(&event).unwrap();
85        Ok(())
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use crate::logbackend::threadmock::MockThreadLogBacked;
92    use crate::logclient::LogClient;
93    use arrayvec::ArrayString;
94    use serde::{Deserialize, Serialize};
95    use std::time::Duration;
96
97    #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
98    pub struct LogOneStruct {
99        some_num: u64,
100        some_other_num: u64,
101        some_string: ArrayString<16>,
102    }
103
104    #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
105    pub struct LogTwoStruct {
106        some_string: ArrayString<16>,
107    }
108
109    #[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
110    #[serde(tag = "t", content = "c")]
111    pub enum LogEvent {
112        LogOneOne(LogOneStruct),
113        LogTwo(LogTwoStruct),
114    }
115
116    #[test]
117    fn create_logger() {
118        let event = LogEvent::LogOneOne(LogOneStruct {
119            some_num: 5,
120            some_other_num: 7,
121            some_string: ArrayString::from("333").unwrap(),
122        });
123
124        let copy_event = event;
125
126        let log_client = LogClient::<_, LogEvent>::new(MockThreadLogBacked::<3000, _>::new());
127
128        log_client.log(event).unwrap();
129
130        std::thread::sleep(Duration::from_millis(200));
131        let rr = log_client.backend().pop_front();
132        assert!(matches!(rr, Some(ev) if ev.data == copy_event));
133
134        std::thread::sleep(Duration::from_millis(200));
135        let rr = log_client.backend().pop_front();
136        assert!(rr.is_none());
137
138        log_client.log(event).unwrap();
139        std::thread::sleep(Duration::from_millis(200));
140        let rr = log_client.backend().pop_front();
141        assert!(matches!(rr, Some(ev) if ev.data == copy_event));
142
143        std::thread::sleep(Duration::from_millis(200));
144        let rr = log_client.backend().pop_front();
145        assert!(rr.is_none());
146    }
147}