blob: 2930c4e379ff80cc8f09077d2a624fe356a3bc6b [file] [log] [blame]
//! 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 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::<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::<salsa::Cycle>().is_some());
}
#[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)
}