use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::JoinHandle;
pub struct Guard {
drain_thread: Option<JoinHandle<()>>,
shutdown: Arc<AtomicBool>,
}
impl Guard {
pub(crate) fn new(drain_thread: JoinHandle<()>, shutdown: Arc<AtomicBool>) -> Self {
Self {
drain_thread: Some(drain_thread),
shutdown,
}
}
}
impl Drop for Guard {
fn drop(&mut self) {
crate::builder::disable_logging();
self.shutdown.store(true, Ordering::Release);
if let Some(handle) = self.drain_thread.take() {
if let Err(e) = handle.join() {
if let Some(msg) = e.downcast_ref::<&str>() {
eprintln!("ticklog: drain thread panicked: {}", msg);
} else if let Some(msg) = e.downcast_ref::<String>() {
eprintln!("ticklog: drain thread panicked: {}", msg);
} else {
eprintln!("ticklog: drain thread panicked");
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::thread;
#[test]
fn new_wraps_handle_and_shutdown() {
let shutdown = Arc::new(AtomicBool::new(false));
let handle = thread::spawn(|| {});
let guard = Guard::new(handle, Arc::clone(&shutdown));
assert!(guard.drain_thread.is_some());
assert!(!guard.shutdown.load(Ordering::Relaxed));
}
#[test]
fn drop_sets_shutdown_flag() {
let shutdown = Arc::new(AtomicBool::new(false));
let flag = Arc::clone(&shutdown);
let handle = thread::spawn(move || {
while !flag.load(Ordering::Acquire) {
std::hint::spin_loop();
}
});
let guard = Guard::new(handle, Arc::clone(&shutdown));
assert!(!shutdown.load(Ordering::Relaxed));
drop(guard);
assert!(shutdown.load(Ordering::Relaxed));
}
#[test]
fn drop_joins_drain_thread() {
let shutdown = Arc::new(AtomicBool::new(false));
let exited = Arc::new(AtomicBool::new(false));
let flag = Arc::clone(&shutdown);
let done = Arc::clone(&exited);
let handle = thread::spawn(move || {
while !flag.load(Ordering::Acquire) {
std::hint::spin_loop();
}
done.store(true, Ordering::Release);
});
let guard = Guard::new(handle, Arc::clone(&shutdown));
drop(guard);
assert!(exited.load(Ordering::Relaxed));
}
#[test]
fn guard_is_send() {
fn assert_send<T: Send>() {}
assert_send::<Guard>();
}
}