1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
//! This module contains PathSync structure to synchronize operations on paths.
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::{Arc, Mutex, RwLock},
};
use crate::{AbsolutePath, Result};
/// When multiple threads try to access the same path (especially in cache operations) we get weird
/// race conditions, This structure is to make those operations atomic and thread safe.
#[derive(Debug, Default)]
pub struct PathSync {
locks: Arc<RwLock<HashMap<PathBuf, Arc<Mutex<()>>>>>,
}
impl PathSync {
/// Create a new PathSync
pub fn new() -> Self {
Self {
locks: Arc::new(RwLock::new(HashMap::new())),
}
}
/// Locks the path and runs the given closure with it.
pub fn with_sync_path(
&self,
path: &Path,
mut f: impl FnMut(&Path) -> Result<()>,
) -> Result<()> {
// Do not lock the whole HashMap
let entry = {
let locks = self.locks.clone();
let mut locks = locks.write()?;
locks
.entry(path.to_path_buf())
.or_insert_with(|| Arc::new(Mutex::new(())))
.clone()
};
let _guard = entry.lock()?;
f(path)?;
Ok(())
}
/// Locks the path and runs the given closure with it.
pub fn with_sync_abs_path(
&self,
path: &AbsolutePath,
mut f: impl FnMut(&AbsolutePath) -> Result<()>,
) -> Result<()> {
{
// Do not lock the whole HashMap
let entry = {
let locks = self.locks.clone();
let mut locks = locks.write()?;
locks
.entry(path.to_path_buf())
.or_insert_with(|| Arc::new(Mutex::new(())))
.clone()
};
let _guard = entry.lock()?;
f(path)?;
}
Ok(())
}
}
/// A thread safe singleton for PathSync
pub type PathSyncSingleton = Arc<Mutex<PathSync>>;