diff --git a/src/derived.rs b/src/derived.rs index fd73f867..17e3db5a 100644 --- a/src/derived.rs +++ b/src/derived.rs @@ -11,6 +11,7 @@ use crate::runtime::Runtime; use crate::runtime::RuntimeId; use crate::runtime::StampedValue; use crate::Database; +use crate::SweepStrategy; use log::{debug, info}; use parking_lot::Mutex; use parking_lot::{RwLock, RwLockUpgradableReadGuard}; @@ -790,7 +791,7 @@ where DB: Database, MP: MemoizationPolicy, { - fn sweep(&self, db: &DB) { + fn sweep(&self, db: &DB, strategy: SweepStrategy) { let mut map_write = self.map.write(); let revision_now = db.salsa_runtime().current_revision(); map_write.retain(|key, query_state| { @@ -820,6 +821,10 @@ where // when we read `revision_now`. assert!(memo.verified_at <= revision_now); + if !strategy.keep_values { + memo.value = None; + } + memo.verified_at == revision_now } } diff --git a/src/input.rs b/src/input.rs index a8054a31..2d73674c 100644 --- a/src/input.rs +++ b/src/input.rs @@ -8,6 +8,7 @@ use crate::runtime::Revision; use crate::runtime::StampedValue; use crate::Database; use crate::Query; +use crate::SweepStrategy; use log::debug; use parking_lot::{RwLock, RwLockUpgradableReadGuard}; use rustc_hash::FxHashMap; @@ -215,7 +216,7 @@ where DB: Database, Q::Value: Default, { - fn sweep(&self, _db: &DB) {} + fn sweep(&self, _db: &DB, _strategy: SweepStrategy) {} } impl InputQueryStorageOps for InputStorage diff --git a/src/lib.rs b/src/lib.rs index 8f4fbc83..0757edb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,8 +47,8 @@ pub trait Database: plumbing::DatabaseStorageTypes + plumbing::DatabaseOps { /// threads that would be performing a `set`). /// /// [`lock_revision`]: struct.Runtime.html#method.lock_revision - fn sweep_all(&self) { - self.salsa_runtime().sweep_all(self); + fn sweep_all(&self, strategy: SweepStrategy) { + self.salsa_runtime().sweep_all(self, strategy); } /// Get access to extra methods pertaining to a given query, @@ -63,6 +63,33 @@ pub trait Database: plumbing::DatabaseStorageTypes + plumbing::DatabaseOps { } } +/// The sweep strategy controls what data we will keep/discard when we +/// do a GC-sweep. The default (`SweepStrategy::default`) is to keep +/// all memoized values used in the current revision. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SweepStrategy { + keep_values: bool, +} + +impl SweepStrategy { + /// Causes us to discard memoized *values* but keep the + /// *dependencies*. This means you will have to recompute the + /// results from any queries you execute but does permit you to + /// quickly determine if a value is still up to date. + pub fn discard_values(self) -> SweepStrategy { + SweepStrategy { + keep_values: false, + ..self + } + } +} + +impl Default for SweepStrategy { + fn default() -> Self { + SweepStrategy { keep_values: true } + } +} + /// Indicates a database that also supports parallel query /// evaluation. All of Salsa's base query support is capable of /// parallel execution, but for it to work, your query key/value types @@ -108,11 +135,11 @@ where }) } - pub fn sweep(&self) + pub fn sweep(&self, strategy: SweepStrategy) where Q::Storage: plumbing::QueryStorageMassOps, { - self.storage.sweep(self.db); + self.storage.sweep(self.db, strategy); } /// Assign a value to an "input query". Must be used outside of diff --git a/src/plumbing.rs b/src/plumbing.rs index b6cd248e..66482ed1 100644 --- a/src/plumbing.rs +++ b/src/plumbing.rs @@ -1,6 +1,7 @@ use crate::Database; use crate::Query; use crate::QueryTable; +use crate::SweepStrategy; use std::fmt::Debug; use std::hash::Hash; @@ -41,7 +42,7 @@ pub trait DatabaseOps: Sized { /// query, unlike `QueryStorageOps`). pub trait QueryStorageMassOps { /// Discards memoized values that are not up to date with the current revision. - fn sweep(&self, db: &DB); + fn sweep(&self, db: &DB, strategy: SweepStrategy); } pub trait QueryDescriptor: Clone + Debug + Eq + Hash + Send + Sync { diff --git a/src/runtime.rs b/src/runtime.rs index 79013271..efdab6af 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,4 +1,4 @@ -use crate::Database; +use crate::{Database, SweepStrategy}; use lock_api::RawRwLock; use log::debug; use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard}; @@ -94,13 +94,13 @@ where } /// Default implementation for `Database::sweep_all`. - pub fn sweep_all(&self, db: &DB) { + pub fn sweep_all(&self, db: &DB, strategy: SweepStrategy) { // Note that we do not acquire the query lock (or any locks) // here. Each table is capable of sweeping itself atomically // and there is no need to bring things to a halt. That said, // users may wish to guarantee atomicity. - db.for_each_query(|query_storage| query_storage.sweep(db)); + db.for_each_query(|query_storage| query_storage.sweep(db, strategy)); } /// Indicates that a derived query has begun to execute; if this is the diff --git a/tests/gc/derived_tests.rs b/tests/gc/derived_tests.rs index 784e9522..53e345c3 100644 --- a/tests/gc/derived_tests.rs +++ b/tests/gc/derived_tests.rs @@ -1,7 +1,7 @@ use crate::db; use crate::group::*; use salsa::debug::DebugQueryTable; -use salsa::Database; +use salsa::{Database, SweepStrategy}; macro_rules! assert_keys { ($db:expr, $($query:expr => ($($key:expr),*),)*) => { @@ -34,7 +34,7 @@ fn compute_one() { // Memoized, but will compute fibonacci(5) again db.compute(5); - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); assert_keys! { db, @@ -72,7 +72,7 @@ fn compute_switch() { Max => (), } - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); // Now we just have `Triangular` and not `Fibonacci` assert_keys! { @@ -88,7 +88,7 @@ fn compute_switch() { // Now run `compute` *again* in next revision. db.salsa_runtime().next_revision(); assert_eq!(db.compute(5), 15); - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); // We keep triangular, but just the outermost one. assert_keys! { @@ -117,7 +117,7 @@ fn compute_all() { db.compute_all(); db.salsa_runtime().next_revision(); db.compute_all(); - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); assert_keys! { db, @@ -145,7 +145,7 @@ fn compute_all() { Max => (()), } - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); // We no longer used `Compute(5)` and `Triangular(5)`; note that // `UseTriangular(5)` is not collected, as it is an input. diff --git a/tests/gc/shallow_constant_tests.rs b/tests/gc/shallow_constant_tests.rs index bda164c3..1659a073 100644 --- a/tests/gc/shallow_constant_tests.rs +++ b/tests/gc/shallow_constant_tests.rs @@ -1,7 +1,7 @@ use crate::db; use crate::group::{Fibonacci, GcDatabase}; use salsa::debug::DebugQueryTable; -use salsa::Database; +use salsa::{Database, SweepStrategy}; // For constant values (like `fibonacci`), we only keep the values // that were used in the latest revision, not the sub-values that @@ -18,7 +18,7 @@ fn one_rev() { // Everything was used in this revision, so // nothing gets collected. - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); assert_eq!(k.len(), 6); } @@ -35,7 +35,7 @@ fn two_rev_nothing() { // Nothing was used in this revision, so // everything gets collected. - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); let k: Vec<_> = db.query(Fibonacci).keys(); assert_eq!(k.len(), 0); @@ -56,7 +56,7 @@ fn two_rev_one_use() { // fibonacci is a constant, so it will not be invalidated, // hence we keep `fibonacci(5)` but remove 0..=4. - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); let k: Vec<_> = db.query(Fibonacci).keys(); assert_eq!(k, vec![5]); @@ -78,7 +78,7 @@ fn two_rev_two_uses() { // fibonacci is a constant, so it will not be invalidated, // hence we keep 3 and 5 but remove the rest. - db.sweep_all(); + db.sweep_all(SweepStrategy::default()); let mut k: Vec<_> = db.query(Fibonacci).keys(); k.sort();