Skip to main content

yapi_core/tools/
mutation.rs

1//! Serializes writes to the same file across concurrently running tool calls.
2
3use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5use std::sync::{Arc, Mutex, OnceLock};
6
7/// Runs `work` once every earlier mutation of the same file (by real path) has
8/// finished.
9pub async fn with_file_lock<T>(path: &Path, work: impl Future<Output = T>) -> T {
10    let _guard = file_lock(path).await;
11    work.await
12}
13
14/// pi's `withFileMutationQueue` for work outside the host: waits until every
15/// earlier mutation of the same file (by real path) has finished, and holds
16/// the file until the guard drops.
17pub async fn file_lock(path: &Path) -> tokio::sync::OwnedMutexGuard<()> {
18    static LOCKS: OnceLock<Mutex<HashMap<PathBuf, Arc<tokio::sync::Mutex<()>>>>> = OnceLock::new();
19    let key = std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf());
20    let lock = {
21        let mut locks = yapi_types::sync::lock(LOCKS.get_or_init(Default::default));
22        locks.retain(|_, lock| Arc::strong_count(lock) > 1);
23        locks.entry(key).or_default().clone()
24    };
25    lock.lock_owned().await
26}