diff --git a/Cargo.toml b/Cargo.toml index eef7509..eb17156 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" ringbuf = "0.2.2" triple_buffer = "5.0.6" +lazy_static = "1.4.0" #hexotk = { optional = true, git = "https://github.com/WeirdConstructor/HexoTK.git" } #hexotk = { optional = true, path = "../hexotk" } hound = "3.4.0" diff --git a/src/lib.rs b/src/lib.rs index a154244..4b692ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -260,7 +260,9 @@ pub mod monitor; pub mod matrix_repr; pub mod sample_lib; mod util; +pub mod log; +pub use log::log; pub use nodes::{new_node_engine, NodeConfigurator, NodeExecutor}; pub use cell_dir::CellDir; pub use matrix::{Matrix, Cell}; diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..b810368 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,148 @@ +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> = { + Arc::new(Mutex::new(LogReceiver::new())) + }; +} + +thread_local! { + pub static LOG: RefCell> = RefCell::new(None); +} + +pub fn retrieve_log_messages(msgs: &mut Vec) { + 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: 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>, +} + +impl LogReceiver { + pub fn new() -> Self { + Self { + consumers: vec![], + } + } + + pub fn retrieve_messages(&mut self, out: &mut Vec) { + 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, + 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)>(&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; + } + }; + } +}