Skip to content

Commit

Permalink
Lock all cargo invocations with an OS semaphore.
Browse files Browse the repository at this point in the history
Using cargo concurrently on the same system often results in badness such as
corruption of the global git database, corruption of the local target directory,
corruption of output artifacts, etc. This is a pretty rare situation, but is
quite troubling to track down when it occurs.

To prevent this badness, cargo needs some method of synchronizing with other
cargo processes. This is often done with a file lock, but sadly file locks are
difficult to use correctly on linux and other flavors of unix [1].

Instead, another mechanism, an OS semaphore, is used. Semaphores provide the
exact functionality that is required here as there's no real need to lock a file
but rather just synchronize with another process. I wrote external bindings for
these semaphores [2] instead of bundling it directly in cargo itself.

Currently only windows, linux, and OSX are supported in ipc-rs, but adding new
platforms with System V compatibility is just a matter of looking up some
constants (nothing too too hard).

[1]: http://0pointer.de/blog/projects/locking.html
[2]: https://github.com/alexcrichton/ipc-rs

Closes rust-lang#354
  • Loading branch information
alexcrichton committed Aug 13, 2014
1 parent 0569c62 commit dbbc696
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 2 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ git = "https://github.com/servo/rust-url"
[dependencies.semver]
git = "https://github.com/rust-lang/semver"

[dependencies.ipc]
git = "https://github.com/alexcrichton/ipc-rs"

[[bin]]
name = "cargo"
test = false
Expand Down
3 changes: 2 additions & 1 deletion src/cargo/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extern crate url;
#[phase(plugin, link)] extern crate log;

extern crate docopt;
extern crate ipc;
extern crate toml;
#[cfg(test)] extern crate hamcrest;

Expand All @@ -30,7 +31,7 @@ use docopt::FlagParser;
use core::{Shell, MultiShell, ShellConfig};
use term::color::{BLACK};

pub use util::{CargoError, CliError, CliResult, human};
pub use util::{CargoError, CliError, CliResult, human, internal};

macro_rules! some(
($e:expr) => (
Expand Down
4 changes: 3 additions & 1 deletion src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use ops;
use sources::{PathSource};
use util::config::{Config, ConfigValue};
use util::{CargoResult, Wrap, config, internal, human, ChainError};
use util::profile;
use util::{profile, global_lock};

pub struct CompileOptions<'a> {
pub update: bool,
Expand All @@ -59,6 +59,8 @@ pub fn compile(manifest_path: &Path,
`cargo update` command instead"));
}

let _guard = try!(global_lock(*shell));

let mut source = try!(PathSource::for_path(&manifest_path.dir_path()));
try!(source.update());

Expand Down
29 changes: 29 additions & 0 deletions src/cargo/util/lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use term;
use ipc::Semaphore;

use util::{CargoResult, ChainError, internal};
use core::MultiShell;

#[must_use]
pub struct Guard {
sem: Semaphore,
}

pub fn global_lock(shell: &mut MultiShell) -> CargoResult<Guard> {
let sem = try!(Semaphore::new("cargo-lock", 1).chain_error(|| {
internal("failed to create the OS semaphore")
}));

if !sem.try_acquire() {
try!(shell.say("Waiting for another cargo process to exit...",
term::color::YELLOW));
sem.acquire()
}
Ok(Guard { sem: sem })
}

impl Drop for Guard {
fn drop(&mut self) {
self.sem.release();
}
}
2 changes: 2 additions & 0 deletions src/cargo/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub use self::dependency_queue::{DependencyQueue, Fresh, Dirty, Freshness};
pub use self::dependency_queue::Dependency;
pub use self::graph::Graph;
pub use self::to_url::ToUrl;
pub use self::lock::{global_lock, Guard};

pub mod graph;
pub mod process_builder;
Expand All @@ -25,3 +26,4 @@ pub mod profile;
mod pool;
mod dependency_queue;
mod to_url;
mod lock;

0 comments on commit dbbc696

Please sign in to comment.