use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
use std::sync::atomic::AtomicU32;
use std::time::Duration;
use std::thread;
use parking_lot::Mutex;
pub struct LockWatchdog {
should_stop: Arc<AtomicBool>,
handle: Mutex<Option<thread::JoinHandle<()>>>,
epoch: Arc<AtomicU32>,
}
impl LockWatchdog {
pub fn new() -> Self {
Self {
should_stop: Arc::new(AtomicBool::new(false)),
handle: Mutex::new(None),
epoch: Arc::new(AtomicU32::new(0)),
}
}
pub fn start<F>(&mut self, renew_interval: Duration, renew_func: F)
where
F: Fn() -> bool + Send + 'static,
{
self.stop();
self.should_stop.store(false, Ordering::SeqCst);
let current_epoch = self.epoch.fetch_add(1, Ordering::SeqCst) + 1;
let should_stop = self.should_stop.clone();
let epoch = self.epoch.clone();
let handle = thread::spawn(move || {
while !should_stop.load(Ordering::SeqCst) {
if epoch.load(Ordering::SeqCst) != current_epoch {
break;
}
if !renew_func() {
break;
}
let sleep_duration = renew_interval;
let mut slept = Duration::from_secs(0);
while slept < sleep_duration && !should_stop.load(Ordering::SeqCst) {
let remaining = sleep_duration - slept;
let chunk = remaining.min(Duration::from_millis(100));
thread::sleep(chunk);
slept += chunk;
if epoch.load(Ordering::SeqCst) != current_epoch {
break;
}
}
if epoch.load(Ordering::SeqCst) != current_epoch {
break;
}
}
});
*self.handle.lock() = Some(handle);
}
pub fn stop(&mut self) {
self.should_stop.store(true, Ordering::SeqCst);
self.epoch.fetch_add(1, Ordering::SeqCst);
let handle = self.handle.lock().take();
if let Some(handle) = handle {
let _ = handle.join();
}
self.should_stop.store(false, Ordering::SeqCst);
}
}
impl Drop for LockWatchdog {
fn drop(&mut self) {
self.stop();
}
}