#
cargo/core/compiler/
locking.rs

    1//! This module handles the locking logic during compilation.
    2
    3use crate::{
    4    CargoResult,
    5    core::compiler::{BuildRunner, Unit},
    6    util::{FileLock, Filesystem},
    7};
    8use anyhow::bail;
    9use std::{
    10    collections::HashMap,
    11    fmt::{Display, Formatter},
    12    path::PathBuf,
    13    sync::Mutex,
    14};
    15use tracing::instrument;
    16
    17/// A struct to store the lock handles for build units during compilation.
    18pub struct LockManager {
    19    locks: Mutex<HashMap<LockKey, FileLock>>,
    20}
    21
    22impl LockManager {
    23    pub fn new() -> Self {
    24        Self {
    25            locks: Mutex::new(HashMap::new()),
    26        }
    27    }
    28
    29    /// Takes a shared lock on a given [`Unit`]
    30    /// This prevents other Cargo instances from compiling (writing) to
    31    /// this build unit.
    32    ///
    33    /// This function returns a [`LockKey`] which can be used to
    34    /// upgrade/unlock the lock.
    35    #[instrument(skip_all, fields(key))]
    36    pub fn lock_shared(
    37        &self,
    38        build_runner: &BuildRunner<'_, '_>,
    39        unit: &Unit,
    40    ) -> CargoResult<LockKey> {
    41        let key = LockKey::from_unit(build_runner, unit);
    42        tracing::Span::current().record("key", key.0.to_str());
    43
    44        let mut locks = self.locks.lock().unwrap();
    45        if let Some(lock) = locks.get_mut(&key) {
    46            lock.file().lock_shared()?;
    47        } else {
    48            let fs = Filesystem::new(key.0.clone());
    49            let lock_msg = format!(
    50                "{} ({})",
    51                unit.pkg.name(),
    52                build_runner.files().unit_hash(unit)
    53            );
    54            let lock = fs.open_ro_shared_create(&key.0, build_runner.bcx.gctx, &lock_msg)?;
    55            locks.insert(key.clone(), lock);
    56        }
    57
    58        Ok(key)
    59    }
    60
    61    #[instrument(skip(self))]
    62    pub fn lock(&self, key: &LockKey) -> CargoResult<()> {
    63        let mut locks = self.locks.lock().unwrap();
    64        if let Some(lock) = locks.get_mut(&key) {
    65            lock.file().lock()?;
    66        } else {
    67            bail!("lock was not found in lock manager: {key}");
    68        }
    69
    70        Ok(())
    71    }
    72
    73    /// Upgrades an existing exclusive lock into a shared lock.
    74    #[instrument(skip(self))]
    75    pub fn downgrade_to_shared(&self, key: &LockKey) -> CargoResult<()> {
    76        let mut locks = self.locks.lock().unwrap();
    77        let Some(lock) = locks.get_mut(key) else {
    78            bail!("lock was not found in lock manager: {key}");
    79        };
    80        lock.file().lock_shared()?;
    81        Ok(())
    82    }
    83
    84    #[instrument(skip(self))]
    85    pub fn unlock(&self, key: &LockKey) -> CargoResult<()> {
    86        let mut locks = self.locks.lock().unwrap();
    87        if let Some(lock) = locks.get_mut(key) {
    88            lock.file().unlock()?;
    89        };
    90
    91        Ok(())
    92    }
    93}
    94
    95#[derive(Debug, Clone, Hash, Eq, PartialEq)]
    96pub struct LockKey(PathBuf);
    97
    98impl LockKey {
    99    fn from_unit(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> Self {
    100        Self(build_runner.files().build_unit_lock(unit))
    101    }
    102}
    103
    104impl Display for LockKey {
    105    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    106        write!(f, "{}", self.0.display())
    107    }
    108}