mirror of
https://github.com/salsa-rs/salsa.git
synced 2024-12-24 12:58:37 +00:00
s/next_revision/synthetic_write/
Also write some docs explaining its side-effects.
This commit is contained in:
parent
a0a6bac5af
commit
9d474363fc
6 changed files with 85 additions and 27 deletions
|
@ -116,18 +116,37 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates that some input to the system has changed and hence
|
/// A "synthetic write" causes the system to act *as though* some
|
||||||
/// that memoized values **may** be invalidated. This cannot be
|
/// input of durability `durability` has changed. This is mostly
|
||||||
/// invoked while query computation is in progress.
|
/// useful for profiling scenarios, but it also has interactions
|
||||||
|
/// with garbage collection. In general, a synthetic write to
|
||||||
|
/// durability level D will cause the system to fully trace all
|
||||||
|
/// queries of durability level D and below. When running a GC, then:
|
||||||
///
|
///
|
||||||
/// As a user of the system, you would not normally invoke this
|
/// - Synthetic writes will cause more derived values to be
|
||||||
/// method directly. Instead, you would use "input" queries and
|
/// *retained*. This is because derived values are only
|
||||||
/// invoke their `set` method. But it can be useful if you have a
|
/// retained if they are traced, and a synthetic write can cause
|
||||||
/// "volatile" input that you must poll from time to time; in that
|
/// more things to be traced.
|
||||||
/// case, you can wrap the input with a "no-storage" query and
|
/// - Synthetic writes can cause more interned values to be
|
||||||
/// invoke this method from time to time.
|
/// *collected*. This is because interned values can only be
|
||||||
pub fn next_revision(&self) {
|
/// collected if they were not yet traced in the current
|
||||||
self.with_incremented_revision(|_| ());
|
/// revision. Therefore, if you issue a synthetic write, execute
|
||||||
|
/// some query Q, and then start collecting interned values, you
|
||||||
|
/// will be able to recycle interned values not used in Q.
|
||||||
|
///
|
||||||
|
/// In general, then, one can do a "full GC" that retains only
|
||||||
|
/// those things that are used by some query Q by (a) doing a
|
||||||
|
/// synthetic write at `Durability::HIGH`, (b) executing the query
|
||||||
|
/// Q and then (c) doing a sweep.
|
||||||
|
///
|
||||||
|
/// **WARNING:** Just like an ordinary write, this method triggers
|
||||||
|
/// cancellation. If you invoke it while a snapshot exists, it
|
||||||
|
/// will block until that snapshot is dropped -- if that snapshot
|
||||||
|
/// is owned by the current thread, this could trigger deadlock.
|
||||||
|
pub fn synthetic_write(&self, durability: Durability) {
|
||||||
|
self.with_incremented_revision(|guard| {
|
||||||
|
guard.mark_durability_as_changed(durability);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default implementation for `Database::sweep_all`.
|
/// Default implementation for `Database::sweep_all`.
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::group::*;
|
use crate::group::*;
|
||||||
use salsa::debug::DebugQueryTable;
|
use salsa::debug::DebugQueryTable;
|
||||||
use salsa::{Database, SweepStrategy};
|
use salsa::{Database, Durability, SweepStrategy};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compute_one() {
|
fn compute_one_write_low() {
|
||||||
let mut db = db::DatabaseImpl::default();
|
let mut db = db::DatabaseImpl::default();
|
||||||
|
|
||||||
// Will compute fibonacci(5)
|
// Will compute fibonacci(5)
|
||||||
db.set_use_triangular(5, false);
|
db.set_use_triangular(5, false);
|
||||||
db.compute(5);
|
db.compute(5);
|
||||||
|
|
||||||
db.salsa_runtime().next_revision();
|
db.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
|
|
||||||
assert_keys! {
|
assert_keys! {
|
||||||
db,
|
db,
|
||||||
|
@ -38,6 +38,44 @@ fn compute_one() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compute_one_write_high() {
|
||||||
|
let mut db = db::DatabaseImpl::default();
|
||||||
|
|
||||||
|
// Will compute fibonacci(5)
|
||||||
|
db.set_use_triangular(5, false);
|
||||||
|
db.compute(5);
|
||||||
|
|
||||||
|
// Doing a synthetic write with durability *high* means that we
|
||||||
|
// will revalidate the things `compute(5)` uses, and hence they
|
||||||
|
// are not discarded.
|
||||||
|
db.salsa_runtime().synthetic_write(Durability::HIGH);
|
||||||
|
|
||||||
|
assert_keys! {
|
||||||
|
db,
|
||||||
|
TriangularQuery => (),
|
||||||
|
FibonacciQuery => (0, 1, 2, 3, 4, 5),
|
||||||
|
ComputeQuery => (5),
|
||||||
|
UseTriangularQuery => (5),
|
||||||
|
MinQuery => (),
|
||||||
|
MaxQuery => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memoized, but will compute fibonacci(5) again
|
||||||
|
db.compute(5);
|
||||||
|
db.sweep_all(SweepStrategy::discard_outdated());
|
||||||
|
|
||||||
|
assert_keys! {
|
||||||
|
db,
|
||||||
|
TriangularQuery => (),
|
||||||
|
FibonacciQuery => (0, 1, 2, 3, 4, 5),
|
||||||
|
ComputeQuery => (5),
|
||||||
|
UseTriangularQuery => (5),
|
||||||
|
MinQuery => (),
|
||||||
|
MaxQuery => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compute_switch() {
|
fn compute_switch() {
|
||||||
let mut db = db::DatabaseImpl::default();
|
let mut db = db::DatabaseImpl::default();
|
||||||
|
@ -78,7 +116,7 @@ fn compute_switch() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now run `compute` *again* in next revision.
|
// Now run `compute` *again* in next revision.
|
||||||
db.salsa_runtime().next_revision();
|
db.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
assert_eq!(db.compute(5), 15);
|
assert_eq!(db.compute(5), 15);
|
||||||
db.sweep_all(SweepStrategy::discard_outdated());
|
db.sweep_all(SweepStrategy::discard_outdated());
|
||||||
|
|
||||||
|
@ -107,7 +145,7 @@ fn compute_all() {
|
||||||
db.set_max(6);
|
db.set_max(6);
|
||||||
|
|
||||||
db.compute_all();
|
db.compute_all();
|
||||||
db.salsa_runtime().next_revision();
|
db.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
db.compute_all();
|
db.compute_all();
|
||||||
db.sweep_all(SweepStrategy::discard_outdated());
|
db.sweep_all(SweepStrategy::discard_outdated());
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::group::{FibonacciQuery, GcDatabase};
|
use crate::group::{FibonacciQuery, GcDatabase};
|
||||||
use salsa::debug::DebugQueryTable;
|
use salsa::debug::DebugQueryTable;
|
||||||
use salsa::{Database, SweepStrategy};
|
use salsa::{Database, Durability, SweepStrategy};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sweep_default() {
|
fn sweep_default() {
|
||||||
|
@ -12,7 +12,7 @@ fn sweep_default() {
|
||||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||||
assert_eq!(k.len(), 6);
|
assert_eq!(k.len(), 6);
|
||||||
|
|
||||||
db.salsa_runtime().next_revision();
|
db.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
|
|
||||||
db.fibonacci(5);
|
db.fibonacci(5);
|
||||||
db.fibonacci(3);
|
db.fibonacci(3);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::group::{FibonacciQuery, GcDatabase};
|
use crate::group::{FibonacciQuery, GcDatabase};
|
||||||
use salsa::debug::DebugQueryTable;
|
use salsa::debug::DebugQueryTable;
|
||||||
use salsa::{Database, SweepStrategy};
|
use salsa::{Database, Durability, SweepStrategy};
|
||||||
|
|
||||||
// For constant values (like `fibonacci`), we only keep the values
|
// For constant values (like `fibonacci`), we only keep the values
|
||||||
// that were used in the latest revision, not the sub-values that
|
// that were used in the latest revision, not the sub-values that
|
||||||
|
@ -31,7 +31,7 @@ fn two_rev_nothing() {
|
||||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||||
assert_eq!(k.len(), 6);
|
assert_eq!(k.len(), 6);
|
||||||
|
|
||||||
db.salsa_runtime().next_revision();
|
db.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
|
|
||||||
// Nothing was used in this revision, so
|
// Nothing was used in this revision, so
|
||||||
// everything gets collected.
|
// everything gets collected.
|
||||||
|
@ -50,7 +50,7 @@ fn two_rev_one_use() {
|
||||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||||
assert_eq!(k.len(), 6);
|
assert_eq!(k.len(), 6);
|
||||||
|
|
||||||
db.salsa_runtime().next_revision();
|
db.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
|
|
||||||
db.fibonacci(5);
|
db.fibonacci(5);
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ fn two_rev_two_uses() {
|
||||||
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
let k: Vec<_> = db.query(FibonacciQuery).entries();
|
||||||
assert_eq!(k.len(), 6);
|
assert_eq!(k.len(), 6);
|
||||||
|
|
||||||
db.salsa_runtime().next_revision();
|
db.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
|
|
||||||
db.fibonacci(5);
|
db.fibonacci(5);
|
||||||
db.fibonacci(3);
|
db.fibonacci(3);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::implementation::{TestContext, TestContextImpl};
|
use crate::implementation::{TestContext, TestContextImpl};
|
||||||
use salsa::Database;
|
use salsa::{Database, Durability};
|
||||||
|
|
||||||
#[salsa::query_group(MemoizedVolatile)]
|
#[salsa::query_group(MemoizedVolatile)]
|
||||||
pub(crate) trait MemoizedVolatileContext: TestContext {
|
pub(crate) trait MemoizedVolatileContext: TestContext {
|
||||||
|
@ -58,7 +58,7 @@ fn revalidate() {
|
||||||
|
|
||||||
// Second generation: volatile will change (to 1) but memoized1
|
// Second generation: volatile will change (to 1) but memoized1
|
||||||
// will not (still 0, as 1/2 = 0)
|
// will not (still 0, as 1/2 = 0)
|
||||||
query.salsa_runtime().next_revision();
|
query.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
query.memoized2();
|
query.memoized2();
|
||||||
query.assert_log(&["Memoized1 invoked", "Volatile invoked"]);
|
query.assert_log(&["Memoized1 invoked", "Volatile invoked"]);
|
||||||
query.memoized2();
|
query.memoized2();
|
||||||
|
@ -67,7 +67,7 @@ fn revalidate() {
|
||||||
// Third generation: volatile will change (to 2) and memoized1
|
// Third generation: volatile will change (to 2) and memoized1
|
||||||
// will too (to 1). Therefore, after validating that Memoized1
|
// will too (to 1). Therefore, after validating that Memoized1
|
||||||
// changed, we now invoke Memoized2.
|
// changed, we now invoke Memoized2.
|
||||||
query.salsa_runtime().next_revision();
|
query.salsa_runtime().synthetic_write(Durability::LOW);
|
||||||
|
|
||||||
query.memoized2();
|
query.memoized2();
|
||||||
query.assert_log(&["Memoized1 invoked", "Volatile invoked", "Memoized2 invoked"]);
|
query.assert_log(&["Memoized1 invoked", "Volatile invoked", "Memoized2 invoked"]);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use crate::implementation::DatabaseImpl;
|
use crate::implementation::DatabaseImpl;
|
||||||
use crate::queries::Database;
|
use crate::queries::Database;
|
||||||
use salsa::Database as _Database;
|
use salsa::Database as _Database;
|
||||||
|
use salsa::Durability;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn memoized_twice() {
|
fn memoized_twice() {
|
||||||
|
@ -19,7 +20,7 @@ fn volatile_twice() {
|
||||||
let v2 = db.volatile(); // volatiles are cached, so 2nd read returns the same
|
let v2 = db.volatile(); // volatiles are cached, so 2nd read returns the same
|
||||||
assert_eq!(v1, v2);
|
assert_eq!(v1, v2);
|
||||||
|
|
||||||
db.salsa_runtime().next_revision(); // clears volatile caches
|
db.salsa_runtime().synthetic_write(Durability::LOW); // clears volatile caches
|
||||||
|
|
||||||
let v3 = db.volatile(); // will re-increment the counter
|
let v3 = db.volatile(); // will re-increment the counter
|
||||||
let v4 = db.volatile(); // second call will be cached
|
let v4 = db.volatile(); // second call will be cached
|
||||||
|
@ -39,7 +40,7 @@ fn intermingled() {
|
||||||
assert_eq!(v1, v3);
|
assert_eq!(v1, v3);
|
||||||
assert_eq!(v2, v4);
|
assert_eq!(v2, v4);
|
||||||
|
|
||||||
db.salsa_runtime().next_revision(); // clears volatile caches
|
db.salsa_runtime().synthetic_write(Durability::LOW); // clears volatile caches
|
||||||
|
|
||||||
let v5 = db.memoized(); // re-executes volatile, caches new result
|
let v5 = db.memoized(); // re-executes volatile, caches new result
|
||||||
let v6 = db.memoized(); // re-use cached result
|
let v6 = db.memoized(); // re-use cached result
|
||||||
|
|
Loading…
Reference in a new issue