mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-26 06:42:48 +00:00
don't recover when not a participant
When a query Q invokes a cycle Q1...Q1 but Q is not a participant in that cycle, Q should not recover! Test that.
This commit is contained in:
parent
b8f628d664
commit
5ebd2211a5
4 changed files with 37 additions and 9 deletions
|
@ -256,8 +256,14 @@ where
|
||||||
crate::plumbing::CycleRecoveryStrategy::Fallback => {
|
crate::plumbing::CycleRecoveryStrategy::Fallback => {
|
||||||
if let Some(c) = active_query.take_cycle() {
|
if let Some(c) = active_query.take_cycle() {
|
||||||
assert!(c.is(&cycle));
|
assert!(c.is(&cycle));
|
||||||
|
Q::cycle_fallback(db, &cycle, &self.key)
|
||||||
|
} else {
|
||||||
|
// we are not a participant in this cycle
|
||||||
|
debug_assert!(!cycle
|
||||||
|
.participant_keys()
|
||||||
|
.any(|k| k == self.database_key_index));
|
||||||
|
cycle.throw()
|
||||||
}
|
}
|
||||||
Q::cycle_fallback(db, &cycle, &self.key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use parking_lot::lock_api::{RawRwLock, RawRwLockRecursive};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use rustc_hash::FxHasher;
|
use rustc_hash::FxHasher;
|
||||||
use std::hash::{BuildHasherDefault, Hash};
|
use std::hash::{BuildHasherDefault, Hash};
|
||||||
|
use std::panic::panic_any;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -363,13 +364,15 @@ impl Runtime {
|
||||||
|
|
||||||
self.local_state.restore_query_stack(from_stack);
|
self.local_state.restore_query_stack(from_stack);
|
||||||
|
|
||||||
if me_recovered || !others_recovered {
|
if me_recovered {
|
||||||
// if me_recovered: If the current thread has recovery, we want to throw
|
// If the current thread has recovery, we want to throw
|
||||||
// so that it can begin.
|
// so that it can begin.
|
||||||
//
|
cycle.throw()
|
||||||
// otherwise, if !others_recorded: then no threads have recovery, so we want
|
} else if others_recovered {
|
||||||
// to throw the cycle so that salsa can abort.
|
// If other threads have recovery but we didn't: return and we will block on them.
|
||||||
cycle.throw();
|
} else {
|
||||||
|
// if nobody has recover, then we panic
|
||||||
|
panic_any(cycle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,8 +127,8 @@ impl LocalState {
|
||||||
// They will not have the `cycle` marker set in their
|
// They will not have the `cycle` marker set in their
|
||||||
// stack frames, so they will just read the fallback value
|
// stack frames, so they will just read the fallback value
|
||||||
// from `Ci+1` and continue on their merry way.
|
// from `Ci+1` and continue on their merry way.
|
||||||
if let Some(cycle) = top_query.cycle.take() {
|
if let Some(cycle) = &top_query.cycle {
|
||||||
cycle.throw()
|
cycle.clone().throw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -480,3 +480,22 @@ fn cycle_multiple() {
|
||||||
)
|
)
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cycle_recovery_set_but_not_participating() {
|
||||||
|
let mut db = DatabaseImpl::default();
|
||||||
|
|
||||||
|
// A --> C -+
|
||||||
|
// ^ |
|
||||||
|
// +--+
|
||||||
|
db.set_a_invokes(CycleQuery::C);
|
||||||
|
db.set_c_invokes(CycleQuery::C);
|
||||||
|
|
||||||
|
// Here we expect C to panic and A not to recover:
|
||||||
|
let r = extract_cycle(|| drop(db.cycle_a()));
|
||||||
|
insta::assert_debug_snapshot!(r.all_participants(&db), @r###"
|
||||||
|
[
|
||||||
|
"cycle_c(())",
|
||||||
|
]
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue