diff --git a/salsa-2022-tests/src/lib.rs b/salsa-2022-tests/src/lib.rs index beb78d28..affbcc4e 100644 --- a/salsa-2022-tests/src/lib.rs +++ b/salsa-2022-tests/src/lib.rs @@ -21,4 +21,12 @@ pub trait HasLogger { let logs = std::mem::replace(&mut *self.logger().logs.lock().unwrap(), vec![]); expected.assert_eq(&format!("{:#?}", logs)); } + + /// Asserts the length of the logs, + /// clearing the logged events. This takes `&mut self` because + /// it is meant to be run from outside any tracked functions. + fn assert_logs_len(&mut self, expected: usize) { + let logs = std::mem::replace(&mut *self.logger().logs.lock().unwrap(), vec![]); + assert_eq!(logs.len(), expected); + } } diff --git a/salsa-2022-tests/tests/lru.rs b/salsa-2022-tests/tests/lru.rs index 59d6fcf1..9c017437 100644 --- a/salsa-2022-tests/tests/lru.rs +++ b/salsa-2022-tests/tests/lru.rs @@ -6,12 +6,13 @@ use std::sync::{ Arc, }; +use salsa_2022_tests::{HasLogger, Logger}; use test_log::test; #[salsa::jar(db = Db)] -struct Jar(MyInput, get_hot_potato, get_volatile); +struct Jar(MyInput, get_hot_potato, get_hot_potato2, get_volatile); -trait Db: salsa::DbWithJar {} +trait Db: salsa::DbWithJar + HasLogger {} #[derive(Debug, PartialEq, Eq)] struct HotPotato(u32); @@ -40,9 +41,16 @@ struct MyInput { #[salsa::tracked(jar = Jar, lru = 32)] fn get_hot_potato(db: &dyn Db, input: MyInput) -> Arc { + db.push_log(format!("get_hot_potato({:?})", input.field(db))); Arc::new(HotPotato::new(input.field(db))) } +#[salsa::tracked(jar = Jar)] +fn get_hot_potato2(db: &dyn Db, input: MyInput) -> u32 { + db.push_log(format!("get_hot_potato2({:?})", input.field(db))); + get_hot_potato(db, input).0 +} + #[salsa::tracked(jar = Jar, lru = 32)] fn get_volatile(db: &dyn Db, _input: MyInput) -> usize { static COUNTER: AtomicUsize = AtomicUsize::new(0); @@ -54,6 +62,7 @@ fn get_volatile(db: &dyn Db, _input: MyInput) -> usize { #[derive(Default)] struct Database { storage: salsa::Storage, + logger: Logger, } impl salsa::Database for Database { @@ -64,6 +73,12 @@ impl salsa::Database for Database { impl Db for Database {} +impl HasLogger for Database { + fn logger(&self) -> &Logger { + &self.logger + } +} + fn load_n_potatoes() -> usize { N_POTATOES.with(|n| n.load(Ordering::SeqCst)) } @@ -147,3 +162,30 @@ fn lru_can_be_changed_at_runtime() { drop(db); assert_eq!(load_n_potatoes(), 0); } + +#[test] +fn lru_keeps_dependency_info() { + let mut db = Database::default(); + let capacity = 32; + + // Invoke `get_hot_potato2` 33 times. This will (in turn) invoke + // `get_hot_potato`, which will trigger LRU after 32 executions. + let inputs: Vec = (0..(capacity + 1)) + .map(|i| MyInput::new(&mut db, i as u32)) + .collect(); + + for (i, input) in inputs.iter().enumerate() { + let x = get_hot_potato2(&db, *input); + assert_eq!(x as usize, i); + } + + // We want to test that calls to `get_hot_potato2` are still considered + // clean. Check that no new executions occur as we go here. + db.assert_logs_len((capacity + 1) * 2); + + // calling `get_hot_potato2(0)` has to check that `get_hot_potato(0)` is still valid; + // even though we've evicted it (LRU), we find that it is still good + let p = get_hot_potato2(&db, *inputs.first().unwrap()); + assert_eq!(p, 0); + db.assert_logs_len(0); +}