Skip to content

Commit 3ef0cbe

Browse files
committed
Add test for panic in nested cycle
1 parent 5813326 commit 3ef0cbe

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

src/function/sync.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ impl<'me> ClaimGuard<'me> {
243243
fn release_panicking(&self) {
244244
let mut syncs = self.sync_table.syncs.lock();
245245
let state = syncs.remove(&self.key_index).expect("key claimed twice?");
246+
tracing::debug!(
247+
"Release claim on {:?} due to panic",
248+
self.database_key_index()
249+
);
246250

247251
self.release(state, WaitResult::Panicked);
248252
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//! Test a deeply nested-cycle scenario where cycles have changing query dependencies.
2+
//!
3+
//! The trick is that different threads call into the same cycle from different entry queries and
4+
//! the cycle heads change over different iterations
5+
//!
6+
//! * Thread 1: `a` -> b -> c
7+
//! * Thread 2: `b`
8+
//! * Thread 3: `d` -> `c`
9+
//! * Thread 4: `e` -> `c`
10+
//!
11+
//! `c` calls:
12+
//! * `d` and `a` in the first few iterations
13+
//! * `d`, `b` and `e` in the last iterations
14+
15+
use crate::sync::thread;
16+
use crate::{Knobs, KnobsDatabase};
17+
use std::panic::catch_unwind;
18+
19+
use salsa::CycleRecoveryAction;
20+
21+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, salsa::Update)]
22+
struct CycleValue(u32);
23+
24+
const MIN: CycleValue = CycleValue(0);
25+
const MAX: CycleValue = CycleValue(3);
26+
27+
#[salsa::tracked(cycle_fn=cycle_fn, cycle_initial=initial)]
28+
fn query_a(db: &dyn KnobsDatabase) -> CycleValue {
29+
db.signal(1);
30+
query_b(db)
31+
}
32+
33+
#[salsa::tracked(cycle_fn=cycle_fn, cycle_initial=initial)]
34+
fn query_b(db: &dyn KnobsDatabase) -> CycleValue {
35+
let c_value = query_c(db);
36+
CycleValue(c_value.0 + 1).min(MAX)
37+
}
38+
39+
#[salsa::tracked(cycle_fn=cycle_fn, cycle_initial=initial)]
40+
fn query_c(db: &dyn KnobsDatabase) -> CycleValue {
41+
let d_value = query_d(db);
42+
43+
if d_value > CycleValue(0) {
44+
let _e_value = query_e(db);
45+
let _b = query_b(db);
46+
db.wait_for(2);
47+
db.signal(3);
48+
panic!("Dragons are real");
49+
} else {
50+
let a_value = query_a(db);
51+
CycleValue(d_value.0.max(a_value.0))
52+
}
53+
}
54+
55+
#[salsa::tracked(cycle_fn=cycle_fn, cycle_initial=initial)]
56+
fn query_d(db: &dyn KnobsDatabase) -> CycleValue {
57+
query_c(db)
58+
}
59+
60+
#[salsa::tracked(cycle_fn=cycle_fn, cycle_initial=initial)]
61+
fn query_e(db: &dyn KnobsDatabase) -> CycleValue {
62+
query_c(db)
63+
}
64+
65+
fn cycle_fn(
66+
_db: &dyn KnobsDatabase,
67+
_value: &CycleValue,
68+
_count: u32,
69+
) -> CycleRecoveryAction<CycleValue> {
70+
CycleRecoveryAction::Iterate
71+
}
72+
73+
fn initial(_db: &dyn KnobsDatabase) -> CycleValue {
74+
MIN
75+
}
76+
77+
#[test_log::test]
78+
fn the_test() {
79+
tracing::debug!("Starting new run");
80+
let db_t1 = Knobs::default();
81+
let db_t2 = db_t1.clone();
82+
let db_t3 = db_t1.clone();
83+
let db_t4 = db_t1.clone();
84+
85+
let t1 = thread::spawn(move || {
86+
let _span = tracing::debug_span!("t1", thread_id = ?thread::current().id()).entered();
87+
88+
let result = query_a(&db_t1);
89+
result
90+
});
91+
let t2 = thread::spawn(move || {
92+
let _span = tracing::debug_span!("t4", thread_id = ?thread::current().id()).entered();
93+
db_t4.wait_for(1);
94+
db_t4.signal(2);
95+
query_b(&db_t4)
96+
});
97+
let t3 = thread::spawn(move || {
98+
let _span = tracing::debug_span!("t2", thread_id = ?thread::current().id()).entered();
99+
db_t2.wait_for(1);
100+
query_d(&db_t2)
101+
});
102+
103+
let r_t1 = t1.join();
104+
let r_t2 = t2.join();
105+
let r_t3 = t3.join();
106+
107+
assert!(r_t1.is_err());
108+
assert!(r_t2.is_err());
109+
assert!(r_t3.is_err());
110+
111+
// Pulling the cycle again at a later point should still result in a panic.
112+
assert!(catch_unwind(|| query_d(&db_t3)).is_err());
113+
}

tests/parallel/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod cycle_ab_peeping_c;
99
mod cycle_nested_deep;
1010
mod cycle_nested_deep_conditional;
1111
mod cycle_nested_deep_conditional_changed;
12+
mod cycle_nested_deep_panic;
1213
mod cycle_nested_three_threads;
1314
mod cycle_nested_three_threads_changed;
1415
mod cycle_panic;

0 commit comments

Comments
 (0)