use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::thread::{self, JoinHandle};
use std::time::{Duration, SystemTime};
use crate::core::{FuseError, FuseResult};
use super::reader::FuseReader;
use super::stats::Stats;
#[cfg(feature = "msgpack")]
use serde::de::DeserializeOwned;
fn read_ignoring_poison<T>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
lock.read().unwrap_or_else(|e| e.into_inner())
}
fn write_ignoring_poison<T>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
lock.write().unwrap_or_else(|e| e.into_inner())
}
fn lock_ignoring_poison<T>(lock: &Mutex<T>) -> MutexGuard<'_, T> {
lock.lock().unwrap_or_else(|e| e.into_inner())
}
macro_rules! impl_reader_proxy {
($ty:ident) => {
impl $ty {
pub fn get_raw(&self, key: impl AsRef<[u8]>) -> FuseResult<Option<Vec<u8>>> {
self.load().get_raw(key)
}
pub fn exists(&self, key: impl AsRef<[u8]>) -> bool {
self.load().exists(key)
}
pub fn len(&self) -> usize {
self.load().len()
}
pub fn is_empty(&self) -> bool {
self.load().is_empty()
}
pub fn keys(&self) -> Vec<String> {
self.load().keys()
}
pub fn keys_raw(&self) -> Vec<Vec<u8>> {
self.load().keys_raw().to_vec()
}
pub fn prefix_raw(
&self,
prefix: impl AsRef<[u8]>,
) -> FuseResult<Vec<(String, Vec<u8>)>> {
self.load().prefix_raw(prefix)
}
pub fn items_raw(&self) -> FuseResult<Vec<(String, Vec<u8>)>> {
self.load().items_raw()
}
pub fn objects_raw(&self) -> FuseResult<Vec<Vec<u8>>> {
self.load().objects_raw()
}
pub fn stats(&self) -> Stats {
self.load().stats()
}
pub fn verify(&self) -> FuseResult<()> {
self.load().verify()
}
pub fn is_valid(&self) -> bool {
self.load().is_valid()
}
#[cfg(feature = "msgpack")]
pub fn get<T: DeserializeOwned>(&self, key: impl AsRef<[u8]>) -> FuseResult<Option<T>> {
self.load().get(key)
}
#[cfg(feature = "msgpack")]
pub fn prefix<T: DeserializeOwned>(
&self,
prefix: impl AsRef<[u8]>,
) -> FuseResult<Vec<(String, T)>> {
self.load().prefix(prefix)
}
#[cfg(feature = "msgpack")]
pub fn items<T: DeserializeOwned>(&self) -> FuseResult<Vec<(String, T)>> {
self.load().items()
}
#[cfg(feature = "msgpack")]
pub fn objects<T: DeserializeOwned>(&self) -> FuseResult<Vec<T>> {
self.load().objects()
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct FileStamp {
modified: Option<SystemTime>,
len: u64,
}
impl FileStamp {
fn read(path: &Path) -> FuseResult<Option<Self>> {
match std::fs::metadata(path) {
Ok(meta) => Ok(Some(Self {
modified: meta.modified().ok(),
len: meta.len(),
})),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(e) => Err(FuseError::Io(format!("stat {}: {e}", path.display()))),
}
}
}
pub struct ReloadableReader {
path: PathBuf,
verify: bool,
current: RwLock<Arc<FuseReader>>,
stamp: Mutex<Option<FileStamp>>,
generation: AtomicU64,
}
impl ReloadableReader {
pub fn open(path: impl AsRef<Path>) -> FuseResult<Self> {
Self::with_verify(path, true)
}
pub fn open_unverified(path: impl AsRef<Path>) -> FuseResult<Self> {
Self::with_verify(path, false)
}
fn with_verify(path: impl AsRef<Path>, verify: bool) -> FuseResult<Self> {
let path = path.as_ref().to_path_buf();
let reader = Self::open_one(&path, verify)?;
let stamp = FileStamp::read(&path)?;
Ok(Self {
path,
verify,
current: RwLock::new(Arc::new(reader)),
stamp: Mutex::new(stamp),
generation: AtomicU64::new(0),
})
}
fn open_one(path: &Path, verify: bool) -> FuseResult<FuseReader> {
if verify {
FuseReader::open(path)
} else {
FuseReader::open_unverified(path)
}
}
pub fn load(&self) -> Arc<FuseReader> {
Arc::clone(&read_ignoring_poison(&self.current))
}
pub fn reload_if_changed(&self) -> FuseResult<bool> {
let mut last = lock_ignoring_poison(&self.stamp);
let Some(current) = FileStamp::read(&self.path)? else {
return Ok(false); };
if *last == Some(current) {
return Ok(false);
}
let fresh = Arc::new(Self::open_one(&self.path, self.verify)?);
*write_ignoring_poison(&self.current) = fresh;
*last = Some(current);
self.generation.fetch_add(1, Ordering::Release);
Ok(true)
}
pub fn reload(&self) -> FuseResult<()> {
let mut last = lock_ignoring_poison(&self.stamp);
let fresh = Arc::new(Self::open_one(&self.path, self.verify)?);
*write_ignoring_poison(&self.current) = fresh;
*last = FileStamp::read(&self.path)?;
self.generation.fetch_add(1, Ordering::Release);
Ok(())
}
#[inline]
pub fn generation(&self) -> u64 {
self.generation.load(Ordering::Acquire)
}
#[inline]
pub fn path(&self) -> &Path {
&self.path
}
#[inline]
pub fn verifies(&self) -> bool {
self.verify
}
}
impl_reader_proxy!(ReloadableReader);
impl std::fmt::Debug for ReloadableReader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReloadableReader")
.field("path", &self.path)
.field("generation", &self.generation())
.field("keys", &self.load().len())
.finish()
}
}
#[derive(Default)]
struct Shutdown {
stopped: Mutex<bool>,
signal: Condvar,
}
impl Shutdown {
fn sleep(&self, timeout: Duration) -> bool {
let stopped = lock_ignoring_poison(&self.stopped);
let (stopped, _) = self
.signal
.wait_timeout_while(stopped, timeout, |stopped| !*stopped)
.unwrap_or_else(|e| e.into_inner());
*stopped
}
fn stop(&self) {
*lock_ignoring_poison(&self.stopped) = true;
self.signal.notify_all();
}
}
type ReloadHook = Box<dyn Fn(&Arc<FuseReader>) + Send + Sync + 'static>;
type ErrorHook = Box<dyn Fn(&FuseError) + Send + Sync + 'static>;
pub struct FuseWatcher {
reader: Arc<ReloadableReader>,
shutdown: Arc<Shutdown>,
handle: Option<JoinHandle<()>>,
}
impl FuseWatcher {
pub fn builder(path: impl AsRef<Path>) -> WatcherBuilder {
WatcherBuilder::new(path)
}
pub fn spawn(path: impl AsRef<Path>, interval: Duration) -> FuseResult<Self> {
Self::builder(path).interval(interval).spawn()
}
#[inline]
pub fn load(&self) -> Arc<FuseReader> {
self.reader.load()
}
#[inline]
pub fn reader(&self) -> &Arc<ReloadableReader> {
&self.reader
}
pub fn reload_now(&self) -> FuseResult<bool> {
self.reader.reload_if_changed()
}
#[inline]
pub fn path(&self) -> &Path {
self.reader.path()
}
#[inline]
pub fn generation(&self) -> u64 {
self.reader.generation()
}
pub fn stop(mut self) {
self.shutdown_and_join();
}
fn shutdown_and_join(&mut self) {
self.shutdown.stop();
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}
impl_reader_proxy!(FuseWatcher);
impl Drop for FuseWatcher {
fn drop(&mut self) {
self.shutdown_and_join();
}
}
impl std::fmt::Debug for FuseWatcher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FuseWatcher")
.field("path", &self.reader.path())
.field("generation", &self.generation())
.field("running", &self.handle.is_some())
.finish()
}
}
pub struct WatcherBuilder {
path: PathBuf,
interval: Duration,
verify: bool,
on_reload: Option<ReloadHook>,
on_error: Option<ErrorHook>,
}
impl WatcherBuilder {
fn new(path: impl AsRef<Path>) -> Self {
Self {
path: path.as_ref().to_path_buf(),
interval: Duration::from_secs(30),
verify: true,
on_reload: None,
on_error: None,
}
}
pub fn interval(mut self, interval: Duration) -> Self {
self.interval = interval;
self
}
pub fn verify(mut self, verify: bool) -> Self {
self.verify = verify;
self
}
pub fn on_reload(mut self, hook: impl Fn(&Arc<FuseReader>) + Send + Sync + 'static) -> Self {
self.on_reload = Some(Box::new(hook));
self
}
pub fn on_error(mut self, hook: impl Fn(&FuseError) + Send + Sync + 'static) -> Self {
self.on_error = Some(Box::new(hook));
self
}
pub fn spawn(self) -> FuseResult<FuseWatcher> {
if self.interval.is_zero() {
return Err(FuseError::InvalidArg(
"watcher interval must be greater than zero".into(),
));
}
let reader = Arc::new(if self.verify {
ReloadableReader::open(&self.path)?
} else {
ReloadableReader::open_unverified(&self.path)?
});
let shutdown = Arc::new(Shutdown::default());
let interval = self.interval;
let on_reload = self.on_reload;
let on_error = self.on_error;
let handle = {
let reader = Arc::clone(&reader);
let shutdown = Arc::clone(&shutdown);
thread::Builder::new()
.name("fusedb-watch".into())
.spawn(move || {
while !shutdown.sleep(interval) {
match reader.reload_if_changed() {
Ok(true) => {
if let Some(hook) = &on_reload {
hook(&reader.load());
}
}
Ok(false) => {}
Err(e) => {
if let Some(hook) = &on_error {
hook(&e);
}
}
}
}
})
.map_err(|e| FuseError::Io(format!("spawning watcher thread: {e}")))?
};
Ok(FuseWatcher {
reader,
shutdown,
handle: Some(handle),
})
}
}
impl std::fmt::Debug for WatcherBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WatcherBuilder")
.field("path", &self.path)
.field("interval", &self.interval)
.field("verify", &self.verify)
.field("on_reload", &self.on_reload.is_some())
.field("on_error", &self.on_error.is_some())
.finish()
}
}