2026-05-20 08:56:49 +02:00
|
|
|
//! Cross-process lock used by providers that materialize binaries.
|
|
|
|
|
//!
|
|
|
|
|
//! Cargo builds and downloads can be requested by multiple local test
|
|
|
|
|
//! processes at the same time. The lock keeps those processes from writing the
|
|
|
|
|
//! same target/cache path concurrently.
|
|
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
|
fs, io,
|
|
|
|
|
path::{Path, PathBuf},
|
|
|
|
|
time::{Duration, Instant},
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-03 17:30:58 +02:00
|
|
|
use tokio::time::sleep;
|
|
|
|
|
|
2026-05-20 08:56:49 +02:00
|
|
|
use super::types::BinaryProviderError;
|
|
|
|
|
|
|
|
|
|
const LOCK_RETRY_DELAY: Duration = Duration::from_millis(200);
|
|
|
|
|
const LOCK_TIMEOUT: Duration = Duration::from_secs(10 * 60);
|
|
|
|
|
|
|
|
|
|
/// File-backed lock removed automatically when dropped.
|
|
|
|
|
pub(super) struct BinaryProviderLock {
|
|
|
|
|
/// Path of the lock file currently owned by this process.
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BinaryProviderLock {
|
2026-08-03 17:30:58 +02:00
|
|
|
pub(super) async fn acquire(path: &Path) -> Result<Self, BinaryProviderError> {
|
2026-05-20 08:56:49 +02:00
|
|
|
if let Some(parent) = path.parent() {
|
|
|
|
|
fs::create_dir_all(parent).map_err(|source| BinaryProviderError::Io {
|
|
|
|
|
path: parent.to_owned(),
|
|
|
|
|
source,
|
|
|
|
|
})?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let started = Instant::now();
|
|
|
|
|
loop {
|
|
|
|
|
match fs::OpenOptions::new()
|
|
|
|
|
.write(true)
|
|
|
|
|
.create_new(true)
|
|
|
|
|
.open(path)
|
|
|
|
|
{
|
|
|
|
|
Ok(_) => {
|
|
|
|
|
return Ok(Self {
|
|
|
|
|
path: path.to_owned(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
Err(source) if source.kind() == io::ErrorKind::AlreadyExists => {
|
|
|
|
|
if started.elapsed() >= LOCK_TIMEOUT {
|
|
|
|
|
return Err(BinaryProviderError::LockTimeout {
|
|
|
|
|
path: path.to_owned(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-03 17:30:58 +02:00
|
|
|
sleep(LOCK_RETRY_DELAY).await;
|
2026-05-20 08:56:49 +02:00
|
|
|
}
|
|
|
|
|
Err(source) => {
|
|
|
|
|
return Err(BinaryProviderError::Io {
|
|
|
|
|
path: path.to_owned(),
|
|
|
|
|
source,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for BinaryProviderLock {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
drop(fs::remove_file(&self.path));
|
|
|
|
|
}
|
|
|
|
|
}
|