Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs')
-rw-r--r--crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs68
1 files changed, 68 insertions, 0 deletions
diff --git a/crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs b/crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs
new file mode 100644
index 0000000000..88d5fee0a2
--- /dev/null
+++ b/crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs
@@ -0,0 +1,68 @@
+//! Test a cycle where no queries recover that occurs across threads.
+//! See the `../cycles.rs` for a complete listing of cycle tests,
+//! both intra and cross thread.
+
+use crate::setup::{Knobs, ParDatabaseImpl};
+use expect_test::expect;
+use ra_salsa::ParallelDatabase;
+
+#[test]
+fn parallel_cycle_none_recover() {
+ let db = ParDatabaseImpl::default();
+ db.knobs().signal_on_will_block.set(3);
+
+ let thread_a = std::thread::spawn({
+ let db = db.snapshot();
+ move || db.a(-1)
+ });
+
+ let thread_b = std::thread::spawn({
+ let db = db.snapshot();
+ move || db.b(-1)
+ });
+
+ // We expect B to panic because it detects a cycle (it is the one that calls A, ultimately).
+ // Right now, it panics with a string.
+ let err_b = thread_b.join().unwrap_err();
+ if let Some(c) = err_b.downcast_ref::<ra_salsa::Cycle>() {
+ expect![[r#"
+ [
+ "parallel::parallel_cycle_none_recover::AQuery::a(-1)",
+ "parallel::parallel_cycle_none_recover::BQuery::b(-1)",
+ ]
+ "#]]
+ .assert_debug_eq(&c.unexpected_participants(&db));
+ } else {
+ panic!("b failed in an unexpected way: {err_b:?}");
+ }
+
+ // We expect A to propagate a panic, which causes us to use the sentinel
+ // type `Canceled`.
+ assert!(thread_a.join().unwrap_err().downcast_ref::<ra_salsa::Cycle>().is_some());
+}
+
+#[ra_salsa::query_group(ParallelCycleNoneRecover)]
+pub(crate) trait TestDatabase: Knobs {
+ fn a(&self, key: i32) -> i32;
+ fn b(&self, key: i32) -> i32;
+}
+
+fn a(db: &dyn TestDatabase, key: i32) -> i32 {
+ // Wait to create the cycle until both threads have entered
+ db.signal(1);
+ db.wait_for(2);
+
+ db.b(key)
+}
+
+fn b(db: &dyn TestDatabase, key: i32) -> i32 {
+ // Wait to create the cycle until both threads have entered
+ db.wait_for(1);
+ db.signal(2);
+
+ // Wait for thread A to block on this thread
+ db.wait_for(3);
+
+ // Now try to execute A
+ db.a(key)
+}