blob: 531aa63226b26118341196353453829709d06b4f [file] [edit]
//! This module handles the locking logic during compilation.
use crate::util::flock;
use crate::{
CargoResult,
compiler::{BuildRunner, Unit},
util::{FileLock, Filesystem},
};
use crate::util::data_structures::HashMap;
use anyhow::bail;
use std::{
fmt::{Display, Formatter},
path::PathBuf,
sync::RwLock,
};
use tracing::instrument;
/// A struct to store the lock handles for build units during compilation.
pub struct LockManager {
locks: RwLock<HashMap<LockKey, FileLock>>,
}
impl LockManager {
pub fn new() -> Self {
Self {
locks: RwLock::new(HashMap::default()),
}
}
/// Takes a shared lock on a given [`Unit`]
/// This prevents other Cargo instances from compiling (writing) to
/// this build unit.
///
/// This function returns a [`LockKey`] which can be used to
/// upgrade/unlock the lock.
#[instrument(skip_all, fields(key))]
pub fn lock_shared(
&self,
build_runner: &BuildRunner<'_, '_>,
unit: &Unit,
) -> CargoResult<LockKey> {
let key = LockKey::from_unit(build_runner, unit);
tracing::Span::current().record("key", key.0.to_str());
let mut locks = self.locks.write().unwrap();
if let Some(lock) = locks.get_mut(&key) {
flock::lock_shared(lock.file())?;
} else {
let fs = Filesystem::new(key.0.clone());
let lock_msg = format!(
"{} ({})",
unit.pkg.name(),
build_runner.files().unit_hash(unit)
);
let lock = fs.open_ro_shared_create(&key.0, build_runner.bcx.gctx, &lock_msg)?;
locks.insert(key.clone(), lock);
}
Ok(key)
}
#[instrument(skip(self))]
pub fn lock(&self, key: &LockKey) -> CargoResult<()> {
let locks = self.locks.read().unwrap();
if let Some(lock) = locks.get(&key) {
flock::lock_exclusive(lock.file())?;
} else {
bail!("lock was not found in lock manager: {key}");
}
Ok(())
}
/// Upgrades an existing exclusive lock into a shared lock.
#[instrument(skip(self))]
pub fn downgrade_to_shared(&self, key: &LockKey) -> CargoResult<()> {
let locks = self.locks.read().unwrap();
let Some(lock) = locks.get(key) else {
bail!("lock was not found in lock manager: {key}");
};
flock::lock_shared(lock.file())?;
Ok(())
}
#[instrument(skip(self))]
pub fn unlock(&self, key: &LockKey) -> CargoResult<()> {
let locks = self.locks.read().unwrap();
if let Some(lock) = locks.get(key) {
flock::unlock(lock.file())?;
};
Ok(())
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct LockKey(PathBuf);
impl LockKey {
fn from_unit(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Self {
Self(build_runner.files().build_unit_lock(unit))
}
}
impl Display for LockKey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.display())
}
}