use std::path::{Path, PathBuf};
use std::sync::{Arc, Condvar, Mutex, Weak};
use std::time::Duration;
pub(crate) const TAKEOVER_CHECK_INTERVAL: Duration = Duration::from_secs(1);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Wake {
Stop,
Poke,
Timeout,
}
#[derive(Default)]
struct State {
stop: bool,
poked: bool,
}
pub(crate) struct Bell {
state: Mutex<State>,
cv: Condvar,
}
impl Bell {
pub(crate) fn new() -> Self {
Self {
state: Mutex::new(State::default()),
cv: Condvar::new(),
}
}
pub(crate) fn wait(&self, timeout: Duration) -> Wake {
let guard = self.state.lock().unwrap_or_else(|e| e.into_inner());
let (mut guard, _) = self
.cv
.wait_timeout_while(guard, timeout, |s| !s.stop && !s.poked)
.unwrap_or_else(|e| e.into_inner());
if guard.stop {
Wake::Stop
} else if guard.poked {
guard.poked = false;
Wake::Poke
} else {
Wake::Timeout
}
}
pub(crate) fn stop(&self) {
self.state.lock().unwrap_or_else(|e| e.into_inner()).stop = true;
self.cv.notify_all();
}
fn poke_now(&self) {
self.state.lock().unwrap_or_else(|e| e.into_inner()).poked = true;
self.cv.notify_all();
}
}
static WATCHERS: Mutex<Vec<(PathBuf, Weak<Bell>)>> = Mutex::new(Vec::new());
pub(crate) fn register(path: PathBuf, bell: &Arc<Bell>) {
let mut watchers = WATCHERS.lock().unwrap_or_else(|e| e.into_inner());
watchers.retain(|(_, w)| w.strong_count() > 0);
watchers.push((path, Arc::downgrade(bell)));
}
pub(crate) fn poke(path: &Path) {
let bells: Vec<Arc<Bell>> = {
let mut watchers = WATCHERS.lock().unwrap_or_else(|e| e.into_inner());
watchers.retain(|(_, w)| w.strong_count() > 0);
watchers
.iter()
.filter(|(p, _)| p.as_path() == path)
.filter_map(|(_, w)| w.upgrade())
.collect()
};
for bell in bells {
bell.poke_now();
}
}