From 263849d14d9b8bb6fa8e98e876303edf4428c050 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sun, 8 Sep 2024 09:31:19 +0200 Subject: [PATCH] Handle recursive aux builds without deadlock --- CHANGELOG.md | 4 ++ src/build_manager.rs | 42 +++++++++++++++++-- tests/integrations/basic-fail/Cargo.stdout | 28 ++++++++++++- .../actual_tests_bless/auxiliary/recursive.rs | 1 + .../actual_tests_bless/recursive_aux_build.rs | 1 + 5 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 tests/integrations/basic-fail/tests/actual_tests_bless/auxiliary/recursive.rs create mode 100644 tests/integrations/basic-fail/tests/actual_tests_bless/recursive_aux_build.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e748938..71f41404 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* aux builds don't hang anymore if they are recursively depending on themselves + ### Changed +* Basic tests now work even without the `rustc` cargo feature + ### Removed ## [0.26.0] - 2024-09-07 diff --git a/src/build_manager.rs b/src/build_manager.rs index 325b0fab..26a68595 100644 --- a/src/build_manager.rs +++ b/src/build_manager.rs @@ -1,6 +1,7 @@ //! Auxiliary and dependency builder. Extendable to custom builds. use std::{ + cell::RefCell, collections::{hash_map::Entry, HashMap}, ffi::OsString, sync::{Arc, OnceLock, RwLock}, @@ -84,6 +85,39 @@ impl BuildManager { }); } let mut lock = self.cache.write().unwrap(); + + thread_local! { + static STACK: RefCell> = RefCell::new(vec![]); + } + STACK.with_borrow_mut(|stack| { + if stack.contains(&description) { + Err(Errored { + command: format!("{description:?}"), + errors: vec![], + stderr: b"recursive build".to_vec(), + stdout: vec![], + }) + } else { + stack.push(description.clone()); + Ok(()) + } + })?; + struct Handle(Arc, ()>>>, String); + + impl std::ops::Deref for Handle { + type Target = OnceLock, ()>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl Drop for Handle { + fn drop(&mut self) { + STACK.with_borrow_mut(|stack| assert_eq!(stack.pop().unwrap(), self.1)) + } + } + let once = match lock.entry(description) { Entry::Occupied(entry) => { if let Some(res) = entry.get().get() { @@ -94,12 +128,14 @@ impl BuildManager { stdout: vec![], }); } - entry.get().clone() + + Handle(entry.get().clone(), entry.key().clone()) } Entry::Vacant(entry) => { let once = Arc::new(OnceLock::new()); - entry.insert(once.clone()); - once + let handle = Handle(once.clone(), entry.key().clone()); + entry.insert(once); + handle } }; drop(lock); diff --git a/tests/integrations/basic-fail/Cargo.stdout b/tests/integrations/basic-fail/Cargo.stdout index d325427e..e1568544 100644 --- a/tests/integrations/basic-fail/Cargo.stdout +++ b/tests/integrations/basic-fail/Cargo.stdout @@ -475,6 +475,8 @@ tests/actual_tests_bless/no_test.rs ... FAILED tests/actual_tests_bless/non_top_level_configs.rs ... FAILED tests/actual_tests_bless/pass.rs ... ok tests/actual_tests_bless/pass_with_annotation.rs ... FAILED +Building aux file tests/actual_tests_bless/auxiliary/recursive.rs ... FAILED +tests/actual_tests_bless/recursive_aux_build.rs ... FAILED tests/actual_tests_bless/revised_revision.rs ... FAILED tests/actual_tests_bless/revisioned_executable.rs (revision `run`) ... ok tests/actual_tests_bless/revisioned_executable.rs (revision `panic`) ... ok @@ -767,6 +769,29 @@ full stdout: +FAILED TEST: tests/actual_tests_bless/recursive_aux_build.rs +command: "$CMD" + +error: aux build failed + --> tests/actual_tests_bless/recursive_aux_build.rs:1:16 + | +1 | //@ aux-build: recursive.rs + | ^^^^^^^^^^^^ tests/actual_tests_bless/recursive_aux_build.rs + | +error: aux build failed + --> tests/actual_tests_bless/auxiliary/recursive.rs:1:16 + | +1 | //@ aux-build: recursive.rs + | ^^^^^^^^^^^^ tests/actual_tests_bless/auxiliary/recursive.rs + | + + +full stderr: +recursive build +full stdout: + + + FAILED TEST: tests/actual_tests_bless/revised_revision.rs command: parse comments @@ -1066,6 +1091,7 @@ FAILURES: tests/actual_tests_bless/no_test.rs tests/actual_tests_bless/non_top_level_configs.rs tests/actual_tests_bless/pass_with_annotation.rs + tests/actual_tests_bless/recursive_aux_build.rs tests/actual_tests_bless/revised_revision.rs tests/actual_tests_bless/revisioned_executable.rs (revision panic.run) tests/actual_tests_bless/revisioned_executable_panic.rs (revision run.run) @@ -1078,7 +1104,7 @@ FAILURES: tests/actual_tests_bless/unknown_revision2.rs tests/actual_tests_bless/wrong_diagnostic_code.rs -test result: FAIL. 23 failed; 24 passed; 3 ignored +test result: FAIL. 24 failed; 24 passed; 3 ignored Building dependencies ... ok tests/actual_tests_bless_yolo/revisions_bad.rs (revision `foo`) ... ok diff --git a/tests/integrations/basic-fail/tests/actual_tests_bless/auxiliary/recursive.rs b/tests/integrations/basic-fail/tests/actual_tests_bless/auxiliary/recursive.rs new file mode 100644 index 00000000..45306a83 --- /dev/null +++ b/tests/integrations/basic-fail/tests/actual_tests_bless/auxiliary/recursive.rs @@ -0,0 +1 @@ +//@ aux-build: recursive.rs diff --git a/tests/integrations/basic-fail/tests/actual_tests_bless/recursive_aux_build.rs b/tests/integrations/basic-fail/tests/actual_tests_bless/recursive_aux_build.rs new file mode 100644 index 00000000..45306a83 --- /dev/null +++ b/tests/integrations/basic-fail/tests/actual_tests_bless/recursive_aux_build.rs @@ -0,0 +1 @@ +//@ aux-build: recursive.rs