2021-08-04 01:58:43 +00:00
|
|
|
// Copyright (c) 2021 Weird Constructor <weirdconstructor@gmail.com>
|
|
|
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
|
|
|
// See README.md and COPYING for details.
|
|
|
|
|
2021-07-24 08:08:37 +00:00
|
|
|
use lazy_static::lazy_static;
|
2022-07-17 09:58:28 +00:00
|
|
|
use ringbuf::{Consumer, Producer, RingBuffer};
|
|
|
|
use std::cell::RefCell;
|
2021-07-24 08:08:37 +00:00
|
|
|
|
|
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
|
|
|
|
lazy_static! {
|
2022-07-17 11:06:56 +00:00
|
|
|
static ref LOG_RECV: Arc<Mutex<LogReceiver>> = Arc::new(Mutex::new(LogReceiver::new()));
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
thread_local! {
|
|
|
|
pub static LOG: RefCell<Option<Log>> = RefCell::new(None);
|
|
|
|
}
|
|
|
|
|
2021-07-24 09:02:43 +00:00
|
|
|
pub fn retrieve_log_messages<F: FnMut(&str, &str)>(f: F) {
|
2021-07-24 08:08:37 +00:00
|
|
|
if let Ok(mut lr) = LOG_RECV.lock() {
|
2021-07-24 09:02:43 +00:00
|
|
|
lr.retrieve_messages(f);
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-24 09:02:43 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn init_thread_logger(name: &'static str) -> bool {
|
|
|
|
if !LOG.with(|l| l.borrow().is_some()) {
|
|
|
|
if let Ok(mut lr) = LOG_RECV.lock() {
|
|
|
|
lr.spawn_global_logger(name);
|
|
|
|
return true;
|
|
|
|
}
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
2021-07-24 09:02:43 +00:00
|
|
|
|
|
|
|
false
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn log<F: Fn(&mut std::io::BufWriter<&mut [u8]>)>(f: F) {
|
|
|
|
use std::borrow::BorrowMut;
|
|
|
|
|
|
|
|
LOG.with(|l| {
|
|
|
|
let mut lh = l.borrow_mut();
|
|
|
|
if let Some(lh) = (*(*lh.borrow_mut())).as_mut() {
|
|
|
|
lh.log(f);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-07-17 09:58:28 +00:00
|
|
|
const MAX_LOG_BUFFER: usize = 4096;
|
2021-07-24 08:08:37 +00:00
|
|
|
|
|
|
|
pub struct LogReceiver {
|
2022-07-17 09:58:28 +00:00
|
|
|
consumers: Vec<(&'static str, Consumer<u8>)>,
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl LogReceiver {
|
|
|
|
pub fn new() -> Self {
|
2022-07-17 09:58:28 +00:00
|
|
|
Self { consumers: vec![] }
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
|
|
|
|
2021-07-24 09:02:43 +00:00
|
|
|
pub fn retrieve_messages<F: FnMut(&str, &str)>(&mut self, mut f: F) {
|
|
|
|
for (name, consumer) in self.consumers.iter_mut() {
|
2021-07-24 08:08:37 +00:00
|
|
|
let mut buf = [0; 1024];
|
|
|
|
let mut oi = 0;
|
|
|
|
|
|
|
|
while let Some(byte) = consumer.pop() {
|
|
|
|
if oi >= buf.len() || byte == 0xFF {
|
2021-07-24 09:02:43 +00:00
|
|
|
f(name, std::str::from_utf8(&buf[0..oi]).unwrap());
|
2021-07-24 08:08:37 +00:00
|
|
|
oi = 0;
|
|
|
|
} else {
|
|
|
|
buf[oi] = byte;
|
|
|
|
oi += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-24 09:02:43 +00:00
|
|
|
pub fn spawn_logger(&mut self, name: &'static str) -> Log {
|
2021-07-24 08:08:37 +00:00
|
|
|
let rb = RingBuffer::new(MAX_LOG_BUFFER);
|
|
|
|
let (producer, con) = rb.split();
|
|
|
|
|
2021-07-24 09:02:43 +00:00
|
|
|
self.consumers.push((name, con));
|
2022-07-17 09:58:28 +00:00
|
|
|
Log { producer, buf: [0; 512] }
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
|
|
|
|
2021-07-24 09:02:43 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn spawn_global_logger(&mut self, name: &'static str) {
|
|
|
|
let hdl = self.spawn_logger(name);
|
2021-07-24 08:08:37 +00:00
|
|
|
LOG.with(move |f| {
|
|
|
|
*f.borrow_mut() = Some(hdl);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Log {
|
|
|
|
producer: Producer<u8>,
|
2022-07-17 09:58:28 +00:00
|
|
|
buf: [u8; 512],
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Log {
|
|
|
|
pub fn log_buf(&mut self, data: &[u8]) {
|
|
|
|
self.producer.push_slice(data);
|
|
|
|
let _ = self.producer.push(0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn log<F: Fn(&mut std::io::BufWriter<&mut [u8]>)>(&mut self, f: F) {
|
|
|
|
self.buf.fill(0xFF);
|
|
|
|
|
|
|
|
let len = {
|
|
|
|
let mut bw = std::io::BufWriter::new(&mut self.buf[..]);
|
|
|
|
f(&mut bw);
|
|
|
|
bw.buffer().len()
|
|
|
|
};
|
|
|
|
|
|
|
|
if len < (self.buf.len() - 1) {
|
|
|
|
// include one 0xFF!
|
|
|
|
self.producer.push_slice(&self.buf[0..len + 1]);
|
|
|
|
} else {
|
|
|
|
self.producer.push_slice(&self.buf[0..len]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn check_threaded_logger() {
|
|
|
|
std::thread::spawn(|| {
|
|
|
|
use std::io::Write;
|
2021-07-24 09:02:43 +00:00
|
|
|
assert!(init_thread_logger("tstlog"));
|
2021-07-24 08:08:37 +00:00
|
|
|
log(|w| write!(w, "Test Log{}!", 1).unwrap());
|
|
|
|
log(|w| write!(w, "Test Log{}!", 2).unwrap());
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut msgs = vec![];
|
|
|
|
for _ in 0..100 {
|
2022-07-17 09:58:28 +00:00
|
|
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
2021-07-24 08:08:37 +00:00
|
|
|
|
2021-07-24 09:02:43 +00:00
|
|
|
retrieve_log_messages(|name, s| msgs.push(name.to_string() + "/" + s));
|
2021-07-24 08:08:37 +00:00
|
|
|
|
|
|
|
if msgs.len() > 1 {
|
2021-07-24 09:02:43 +00:00
|
|
|
assert_eq!(msgs[0], "tstlog/Test Log1!");
|
|
|
|
assert_eq!(msgs[1], "tstlog/Test Log2!");
|
2021-07-24 08:08:37 +00:00
|
|
|
break;
|
|
|
|
}
|
2022-07-17 09:58:28 +00:00
|
|
|
}
|
2021-07-24 08:08:37 +00:00
|
|
|
}
|
|
|
|
}
|