HexoDSP/src/log.rs

149 lines
3.4 KiB
Rust
Raw Normal View History

use std::cell::RefCell;
use ringbuf::{RingBuffer, Producer, Consumer};
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
lazy_static! {
static ref LOG_RECV: Arc<Mutex<LogReceiver>> = {
Arc::new(Mutex::new(LogReceiver::new()))
};
}
thread_local! {
pub static LOG: RefCell<Option<Log>> = RefCell::new(None);
}
pub fn retrieve_log_messages(msgs: &mut Vec<String>) {
if let Ok(mut lr) = LOG_RECV.lock() {
lr.retrieve_messages(msgs);
}
}
pub fn init_thread_logger() {
if let Ok(mut lr) = LOG_RECV.lock() {
lr.spawn_global_logger();
}
}
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);
}
});
}
const MAX_LOG_BUFFER : usize = 4096;
pub struct LogReceiver {
consumers: Vec<Consumer<u8>>,
}
impl LogReceiver {
pub fn new() -> Self {
Self {
consumers: vec![],
}
}
pub fn retrieve_messages(&mut self, out: &mut Vec<String>) {
for consumer in self.consumers.iter_mut() {
let mut buf = [0; 1024];
let mut oi = 0;
while let Some(byte) = consumer.pop() {
if oi >= buf.len() || byte == 0xFF {
out.push(
std::str::from_utf8(&buf[0..oi])
.unwrap()
.to_string());
oi = 0;
} else {
buf[oi] = byte;
oi += 1;
}
}
}
}
pub fn spawn_logger(&mut self) -> Log {
let rb = RingBuffer::new(MAX_LOG_BUFFER);
let (producer, con) = rb.split();
self.consumers.push(con);
Log {
producer,
buf: [0; 512],
}
}
pub fn spawn_global_logger(&mut self) {
let hdl = self.spawn_logger();
LOG.with(move |f| {
*f.borrow_mut() = Some(hdl);
});
}
}
pub struct Log {
producer: Producer<u8>,
buf: [u8; 512],
}
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;
init_thread_logger();
log(|w| write!(w, "Test Log{}!", 1).unwrap());
log(|w| write!(w, "Test Log{}!", 2).unwrap());
});
let mut msgs = vec![];
for _ in 0..100 {
std::thread::sleep(
std::time::Duration::from_millis(100));
retrieve_log_messages(&mut msgs);
if msgs.len() > 1 {
assert_eq!(msgs[0], "Test Log1!");
assert_eq!(msgs[1], "Test Log2!");
break;
}
};
}
}