ring_file/
threads.rs

1use std::io::Write;
2use crate::RingBuffer;
3use std::path::Path;
4use crossbeam_channel::*;
5use std::thread;
6
7enum Msg {
8    Clear,
9    Dump,
10    Msg(String),
11}
12
13/// RingFile use a backend thread to maintain RingBuffer, which receive messages with unbounded channel,
14/// to prevent lock contention affecting program execution.
15/// When program hang or panic, you can call dump() to collect the logs into file.
16pub struct RingFile {
17    tx: Sender<Msg>,
18    res: Receiver<std::io::Result<()>>,
19    _th: thread::JoinHandle<()>,
20}
21
22struct RingFileBackend {
23    file_path: Box<Path>,
24    buffer: RingBuffer,
25    rx: Receiver<Msg>,
26    res: Sender<std::io::Result<()>>,
27}
28
29impl RingFileBackend {
30
31    #[inline(always)]
32    fn process(&mut self, msg: Msg) {
33        match msg {
34            Msg::Clear=>{
35                self.buffer.clear();
36            }
37            Msg::Dump=>{
38                let res = self.buffer.dump(self.file_path.as_ref());
39                self.res.send(res).expect("send res");
40            }
41            Msg::Msg(line)=>{
42                let _ = self.buffer.write_all(line.as_bytes());
43            }
44        }
45    }
46
47    fn run(&mut self) {
48        loop {
49            match self.rx.recv() {
50                Ok(msg)=>{
51                    self.process(msg);
52                    while let Ok(msg) = self.rx.try_recv() {
53                        self.process(msg);
54                    }
55                }
56                Err(_)=>{
57                    return;
58                }
59            }
60        }
61    }
62}
63
64impl RingFile {
65    /// # Arguments:
66    ///
67    /// - buf_size: total buffer size
68    ///
69    /// - file_path: The target file to dump
70    pub fn new(buf_size: i32, file_path: Box<Path>) -> Self {
71        let (tx, rx) = crossbeam_channel::unbounded();
72        let (res_tx, res_rx) = crossbeam_channel::bounded(1);
73        let mut backend = RingFileBackend {
74            file_path,
75            buffer: RingBuffer::new(buf_size),
76            rx,
77            res: res_tx,
78        };
79        let _th = thread::spawn(move || backend.run());
80        Self{
81            tx,
82            _th,
83            res: res_rx,
84        }
85    }
86
87    /// Trigger dump to the disk.
88    pub fn dump(&self) -> std::io::Result<()> {
89        self.tx.send(Msg::Dump).expect("send");
90        self.res.recv().unwrap()
91    }
92
93    #[inline(always)]
94    pub fn write(&self, content: String) {
95        self.tx.send(Msg::Msg(content)).expect("send");
96    }
97
98    /// Clear previous buffer
99    pub fn clear(&self) {
100        self.tx.send(Msg::Clear).expect("send");
101    }
102}