Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/cargo/core/compiler/locking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ use std::{
collections::HashMap,
fmt::{Display, Formatter},
path::PathBuf,
sync::Mutex,
sync::RwLock,
};
use tracing::instrument;

/// A struct to store the lock handles for build units during compilation.
pub struct LockManager {
locks: Mutex<HashMap<LockKey, FileLock>>,
locks: RwLock<HashMap<LockKey, FileLock>>,
}

impl LockManager {
pub fn new() -> Self {
Self {
locks: Mutex::new(HashMap::new()),
locks: RwLock::new(HashMap::new()),
}
}

Expand All @@ -41,7 +41,7 @@ impl LockManager {
let key = LockKey::from_unit(build_runner, unit);
tracing::Span::current().record("key", key.0.to_str());

let mut locks = self.locks.lock().unwrap();
let mut locks = self.locks.write().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that a single blocking unit can cause other units from running.

Isn't this still true?

If a job is blocked, they will be holding a Read Lock. New jobs will try to grab the Write lock and be blocked.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LockManager::lock_shared is only called prior to reading the fingerprint (ref)
It is called for all units synchronously while preparing the jobs.

By the time we start executing jobs, all units should have already taken a shared lock and released it.
So there should be no writers after the fingerprint check phase of the build.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, for some reason, I've always assumed we did fingerprint calculations during the build and never looked too closely at the timing and operation of compile.

if let Some(lock) = locks.get_mut(&key) {
lock.file().lock_shared()?;
} else {
Expand All @@ -60,8 +60,8 @@ impl LockManager {

#[instrument(skip(self))]
pub fn lock(&self, key: &LockKey) -> CargoResult<()> {
let mut locks = self.locks.lock().unwrap();
if let Some(lock) = locks.get_mut(&key) {
let locks = self.locks.read().unwrap();
if let Some(lock) = locks.get(&key) {
lock.file().lock()?;
} else {
bail!("lock was not found in lock manager: {key}");
Expand All @@ -73,8 +73,8 @@ impl LockManager {
/// Upgrades an existing exclusive lock into a shared lock.
#[instrument(skip(self))]
pub fn downgrade_to_shared(&self, key: &LockKey) -> CargoResult<()> {
let mut locks = self.locks.lock().unwrap();
let Some(lock) = locks.get_mut(key) else {
let locks = self.locks.read().unwrap();
let Some(lock) = locks.get(key) else {
bail!("lock was not found in lock manager: {key}");
};
lock.file().lock_shared()?;
Expand All @@ -83,8 +83,8 @@ impl LockManager {

#[instrument(skip(self))]
pub fn unlock(&self, key: &LockKey) -> CargoResult<()> {
let mut locks = self.locks.lock().unwrap();
if let Some(lock) = locks.get_mut(key) {
let locks = self.locks.read().unwrap();
if let Some(lock) = locks.get(key) {
lock.file().unlock()?;
};

Expand Down