Skip to content
Merged
Show file tree
Hide file tree
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
6 changes: 5 additions & 1 deletion crates/cargo-gpu-install/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,15 @@ pub struct FileLock(std::fs::File);

impl FileLock {
pub fn lock(path: &Path) -> std::io::Result<Self> {
Self::lock_with_message(path, "codegen backend build")
}

pub fn lock_with_message(path: &Path, what: &str) -> std::io::Result<Self> {
let file = std::fs::File::create(path)?;
match file.try_lock() {
Ok(_) => (),
Err(std::fs::TryLockError::WouldBlock) => {
user_output!("Waiting for file lock on codegen backend build...\n");
user_output!("Waiting for file lock on {what}...\n");
file.lock()?;
}
Err(std::fs::TryLockError::Error(e)) => return Err(e),
Expand Down
24 changes: 24 additions & 0 deletions crates/cargo-gpu-install/src/install_toolchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ pub fn ensure_toolchain_and_components_exist(
let host_tuple = host_tuple.trim_ascii();
let toolchain = format!("{channel}-{host_tuple}");

// Two concurrent `cargo-gpu` processes (e.g. build scripts of multiple shader crates running
// in parallel) can otherwise observe a toolchain that another process is still in the middle
// of installing: `rustup toolchain list` already lists it, but its manifest isn't written yet,
// and the `component list` below fails with `error: Missing manifest in toolchain '...'`.
//
// So hold a lock across the entire check-and-install. It is taken unconditionally, even when
// the codegen backend is already built and the `install_dir` lock in `install.rs` is never
// taken at all, since we always query `rustup` here.
let _rustup_lock = crate::install::FileLock::lock_with_message(&rustup_lock_path()?, "rustup")
.context("acquiring rustup file lock")?;

if !is_toolchain_installed(&toolchain, host_tuple)? {
let message = format!(
"toolchain {channel} with components {}",
Expand Down Expand Up @@ -72,6 +83,19 @@ pub fn ensure_toolchain_and_components_exist(
Ok(())
}

/// Path of the file lock guarding our `rustup` invocations.
fn rustup_lock_path() -> anyhow::Result<std::path::PathBuf> {
let (home, _) = run_cmd(Command::new("rustup").args(["show", "home"]))?;
// FNV-1a, needs to be stable across Rust versions
let hash = home
.trim_ascii()
.bytes()
.fold(0xcbf2_9ce4_8422_2325_u64, |hash, byte| {
(hash ^ u64::from(byte)).wrapping_mul(0x0000_0100_0000_01b3)
});
Ok(std::env::temp_dir().join(format!("rust-gpu-rustup-{hash:016x}.lock")))
}

/// Returns true if the toolchain and required components are installed.
fn is_toolchain_installed(toolchain: &str, host_tuple: &str) -> anyhow::Result<bool> {
// check if toolchain is installed
Expand Down
3 changes: 2 additions & 1 deletion crates/cargo-gpu-install/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ macro_rules! user_output {
#[macro_export]
#[cfg(not(feature = "tty"))]
macro_rules! user_output {
($($args: tt)*) => {{}};
// reference the args, so they don't count as unused when output is compiled out
($($args: tt)*) => {{ let _ = format_args!($($args)*); }};
}

/// The central cache directory of cargo gpu
Expand Down
Loading