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.rs | 68 |
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) +} |