salsa/tests/backtrace.rs
Micha Reiser 2b5188778e
Some checks are pending
Test / Test (push) Waiting to run
Test / Miri (push) Waiting to run
Test / Shuttle (push) Waiting to run
Test / Benchmarks (push) Waiting to run
Book / Book (push) Waiting to run
Book / Deploy (push) Blocked by required conditions
Release-plz / Release-plz release (push) Waiting to run
Release-plz / Release-plz PR (push) Waiting to run
fix: multithreaded nested fixpoint iteration (#882)
* Set `validate_final` in `execute` after removing the last cycle head

* Add runaway query repro

* Add tracing

* Fix part 1

* Fix `cycle_head_kinds` to always return provisional for memos that aren't verified final (They should be validated by `validate_same_iteration` or wait for the cycle head

* Fix cycle error

* Documentation

* Fix await for queries depending on initial value

* correctly initialize queued

* Cleanup

* Short circuit if entire query runs on single thread

* Move parallel code into its own method

* Rename method, add self_key to queued

* Revert self-key changes

* Move check *after* `deep_verify_memo`

* Add a test for a cycle with changing cycle heads

* Short circuit more often

* Consider iteration in `validate_provisional`

* Only yield if all heads result in a cycle. Retry if even just one inner cycle made progress (in which case there's a probably a new memo)

* Fix hangs

* Cargo fmt

* clippy

* Fix hang if cycle initial panics

* Rename `cycle_head_kind` enable `cycle_a_t1_b_t2_fallback` shuttle test

* Cleanup

* Docs
2025-06-01 08:45:37 +00:00

127 lines
3.6 KiB
Rust

use expect_test::expect;
use salsa::{Backtrace, Database, DatabaseImpl};
use test_log::test;
#[salsa::input(debug)]
struct Thing {
detailed: bool,
}
#[salsa::tracked]
fn query_a(db: &dyn Database, thing: Thing) -> String {
query_b(db, thing)
}
#[salsa::tracked]
fn query_b(db: &dyn Database, thing: Thing) -> String {
query_c(db, thing)
}
#[salsa::tracked]
fn query_c(db: &dyn Database, thing: Thing) -> String {
query_d(db, thing)
}
#[salsa::tracked]
fn query_d(db: &dyn Database, thing: Thing) -> String {
query_e(db, thing)
}
#[salsa::tracked]
fn query_e(db: &dyn Database, thing: Thing) -> String {
if thing.detailed(db) {
format!("{:#}", Backtrace::capture().unwrap())
} else {
format!("{}", Backtrace::capture().unwrap())
}
}
#[salsa::tracked]
fn query_f(db: &dyn Database, thing: Thing) -> String {
query_cycle(db, thing)
}
#[salsa::tracked(cycle_fn=cycle_fn, cycle_initial=cycle_initial)]
fn query_cycle(db: &dyn Database, thing: Thing) -> String {
let backtrace = query_cycle(db, thing);
if backtrace.is_empty() {
query_e(db, thing)
} else {
backtrace
}
}
fn cycle_initial(_db: &dyn salsa::Database, _thing: Thing) -> String {
String::new()
}
fn cycle_fn(
_db: &dyn salsa::Database,
_value: &str,
_count: u32,
_thing: Thing,
) -> salsa::CycleRecoveryAction<String> {
salsa::CycleRecoveryAction::Iterate
}
#[test]
fn backtrace_works() {
let db = DatabaseImpl::default();
let backtrace = query_a(&db, Thing::new(&db, false)).replace("\\", "/");
expect![[r#"
query stacktrace:
0: query_e(Id(0))
at tests/backtrace.rs:30
1: query_d(Id(0))
at tests/backtrace.rs:25
2: query_c(Id(0))
at tests/backtrace.rs:20
3: query_b(Id(0))
at tests/backtrace.rs:15
4: query_a(Id(0))
at tests/backtrace.rs:10
"#]]
.assert_eq(&backtrace);
let backtrace = query_a(&db, Thing::new(&db, true)).replace("\\", "/");
expect![[r#"
query stacktrace:
0: query_e(Id(1)) -> (R1, Durability::LOW)
at tests/backtrace.rs:30
1: query_d(Id(1)) -> (R1, Durability::HIGH)
at tests/backtrace.rs:25
2: query_c(Id(1)) -> (R1, Durability::HIGH)
at tests/backtrace.rs:20
3: query_b(Id(1)) -> (R1, Durability::HIGH)
at tests/backtrace.rs:15
4: query_a(Id(1)) -> (R1, Durability::HIGH)
at tests/backtrace.rs:10
"#]]
.assert_eq(&backtrace);
let backtrace = query_f(&db, Thing::new(&db, false)).replace("\\", "/");
expect![[r#"
query stacktrace:
0: query_e(Id(2))
at tests/backtrace.rs:30
1: query_cycle(Id(2))
at tests/backtrace.rs:43
cycle heads: query_cycle(Id(2)) -> IterationCount(0)
2: query_f(Id(2))
at tests/backtrace.rs:38
"#]]
.assert_eq(&backtrace);
let backtrace = query_f(&db, Thing::new(&db, true)).replace("\\", "/");
expect![[r#"
query stacktrace:
0: query_e(Id(3)) -> (R1, Durability::LOW)
at tests/backtrace.rs:30
1: query_cycle(Id(3)) -> (R1, Durability::HIGH, iteration = IterationCount(0))
at tests/backtrace.rs:43
cycle heads: query_cycle(Id(3)) -> IterationCount(0)
2: query_f(Id(3)) -> (R1, Durability::HIGH)
at tests/backtrace.rs:38
"#]]
.assert_eq(&backtrace);
}