Configure bundle directory save locks

This commit is contained in:
Dylan M. Taylor committed 2026-11-13 02:27:57 -05:00
1 parent c0df5be07d
commit de724aa188
1 file changed
+24 -3
+24 -3
View File
@@ -9,7 +9,7 @@ use std::collections::{BTreeMap, HashMap, HashSet};
use std::fs::{self, OpenOptions};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use std::time::{Duration, Instant, SystemTime};
use craft_zip::{Archive, Compression, Writer};
@@ -327,14 +327,20 @@ pub struct DirectoryStats {
}
/// Incremental directory-bundle persistence with bounded rolling object verification.
#[derive(Default)]
pub struct DirectoryBundleWriter {
root: Option<PathBuf>,
generation: u64,
reverify_budget: u64,
lock_wait: Duration,
verified: HashMap<String, CachedFile>,
}
impl Default for DirectoryBundleWriter {
fn default() -> Self {
Self { root: None, generation: 0, reverify_budget: 16 << 20, lock_wait: Duration::from_secs(10), verified: HashMap::new() }
}
}
#[derive(Clone, Copy)]
struct CachedFile {
signature: FileSignature,
@@ -356,6 +362,11 @@ impl DirectoryBundleWriter {
self.reverify_budget = bytes;
}
/// How long to wait for another writer to release the bundle lock.
pub fn set_lock_wait(&mut self, wait: Duration) {
self.lock_wait = wait;
}
/// Saves an entry set incrementally. Existing objects are reused only after a hash check;
/// unchanged file signatures may skip checks until they enter the rolling verification
/// window. The manifest is atomically published last, then stale previews and objects are
@@ -379,7 +390,17 @@ impl DirectoryBundleWriter {
let lock_path = root.join(craft_pcraft_format::SAVE_LOCK);
refuse_symlink(&lock_path)?;
let lock = OpenOptions::new().read(true).write(true).create(true).truncate(false).open(&lock_path).map_err(io_error)?;
lock.lock().map_err(io_error)?;
let start = Instant::now();
let _lock = loop {
match lock.try_lock() {
Ok(()) => break Some(lock),
Err(std::fs::TryLockError::WouldBlock) if start.elapsed() < self.lock_wait => std::thread::sleep(Duration::from_millis(20)),
Err(std::fs::TryLockError::WouldBlock) => {
return Err(Error::Io("another save of this bundle is in progress".into()));
}
Err(std::fs::TryLockError::Error(_)) => break None,
}
};
self.generation = self.generation.saturating_add(1);
let generation = self.generation;
let recheck = self.rolling_recheck(&bundle.entries);