mirror of
https://github.com/salsa-rs/salsa.git
synced 2024-12-04 21:45:55 +00:00
factor out ChangedAt
to record when something changed
This commit is contained in:
parent
4efbd411fa
commit
296d33aae7
4 changed files with 71 additions and 35 deletions
11
src/input.rs
11
src/input.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::runtime::ChangedAt;
|
||||||
use crate::runtime::QueryDescriptorSet;
|
use crate::runtime::QueryDescriptorSet;
|
||||||
use crate::runtime::Revision;
|
use crate::runtime::Revision;
|
||||||
use crate::runtime::StampedValue;
|
use crate::runtime::StampedValue;
|
||||||
|
@ -66,7 +67,7 @@ where
|
||||||
|
|
||||||
Ok(StampedValue {
|
Ok(StampedValue {
|
||||||
value: <Q::Value>::default(),
|
value: <Q::Value>::default(),
|
||||||
changed_at: Revision::ZERO,
|
changed_at: ChangedAt::Revision(Revision::ZERO),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,10 +110,10 @@ where
|
||||||
map_read
|
map_read
|
||||||
.get(key)
|
.get(key)
|
||||||
.map(|v| v.changed_at)
|
.map(|v| v.changed_at)
|
||||||
.unwrap_or(Revision::ZERO)
|
.unwrap_or(ChangedAt::Revision(Revision::ZERO))
|
||||||
};
|
};
|
||||||
|
|
||||||
changed_at > revision
|
changed_at.changed_since(revision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +132,7 @@ where
|
||||||
// racing with somebody else to modify this same cell.
|
// racing with somebody else to modify this same cell.
|
||||||
// (Otherwise, someone else might write a *newer* revision
|
// (Otherwise, someone else might write a *newer* revision
|
||||||
// into the same cell while we block on the lock.)
|
// into the same cell while we block on the lock.)
|
||||||
let changed_at = db.salsa_runtime().increment_revision();
|
let changed_at = ChangedAt::Revision(db.salsa_runtime().increment_revision());
|
||||||
|
|
||||||
map_write.insert(key, StampedValue { value, changed_at });
|
map_write.insert(key, StampedValue { value, changed_at });
|
||||||
}
|
}
|
||||||
|
@ -150,7 +151,7 @@ where
|
||||||
|
|
||||||
// Unlike with `set`, here we use the **current revision** and
|
// Unlike with `set`, here we use the **current revision** and
|
||||||
// do not create a new one.
|
// do not create a new one.
|
||||||
let changed_at = db.salsa_runtime().current_revision();
|
let changed_at = ChangedAt::Revision(db.salsa_runtime().current_revision());
|
||||||
|
|
||||||
map_write.insert(key, StampedValue { value, changed_at });
|
map_write.insert(key, StampedValue { value, changed_at });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::runtime::ChangedAt;
|
||||||
use crate::runtime::QueryDescriptorSet;
|
use crate::runtime::QueryDescriptorSet;
|
||||||
use crate::runtime::Revision;
|
use crate::runtime::Revision;
|
||||||
use crate::runtime::StampedValue;
|
use crate::runtime::StampedValue;
|
||||||
|
@ -91,7 +92,7 @@ where
|
||||||
{
|
{
|
||||||
/// Last time the value has actually changed.
|
/// Last time the value has actually changed.
|
||||||
/// changed_at can be less than verified_at.
|
/// changed_at can be less than verified_at.
|
||||||
changed_at: Revision,
|
changed_at: ChangedAt,
|
||||||
|
|
||||||
/// The result of the query, if we decide to memoize it.
|
/// The result of the query, if we decide to memoize it.
|
||||||
value: Option<Q::Value>,
|
value: Option<Q::Value>,
|
||||||
|
@ -184,24 +185,18 @@ where
|
||||||
// first things first, let's walk over each of our previous
|
// first things first, let's walk over each of our previous
|
||||||
// inputs and check whether they are out of date.
|
// inputs and check whether they are out of date.
|
||||||
if let Some(QueryState::Memoized(old_memo)) = &mut old_value {
|
if let Some(QueryState::Memoized(old_memo)) = &mut old_value {
|
||||||
if old_memo.value.is_some() {
|
if old_memo.validate_memoized_value(db) {
|
||||||
if old_memo
|
debug!("{:?}({:?}): inputs still valid", Q::default(), key);
|
||||||
.inputs
|
// If none of out inputs have changed since the last time we refreshed
|
||||||
.iter()
|
// our value, then our value must still be good. We'll just patch
|
||||||
.all(|old_input| !old_input.maybe_changed_since(db, old_memo.changed_at))
|
// the verified-at date and re-use it.
|
||||||
{
|
old_memo.verified_at = revision_now;
|
||||||
debug!("{:?}({:?}): inputs still valid", Q::default(), key);
|
let value = old_memo.value.clone().unwrap();
|
||||||
// If none of out inputs have changed since the last time we refreshed
|
let changed_at = old_memo.changed_at;
|
||||||
// our value, then our value must still be good. We'll just patch
|
|
||||||
// the verified-at date and re-use it.
|
|
||||||
old_memo.verified_at = revision_now;
|
|
||||||
let value = old_memo.value.clone().unwrap();
|
|
||||||
let changed_at = old_memo.changed_at;
|
|
||||||
|
|
||||||
let mut map_write = self.map.write();
|
let mut map_write = self.map.write();
|
||||||
self.overwrite_placeholder(&mut map_write, key, old_value.unwrap());
|
self.overwrite_placeholder(&mut map_write, key, old_value.unwrap());
|
||||||
return Ok(StampedValue { value, changed_at });
|
return Ok(StampedValue { value, changed_at });
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,14 +313,14 @@ where
|
||||||
// If our memo is still up to date, then check if we've
|
// If our memo is still up to date, then check if we've
|
||||||
// changed since the revision.
|
// changed since the revision.
|
||||||
if memo.verified_at == revision_now {
|
if memo.verified_at == revision_now {
|
||||||
return memo.changed_at > revision;
|
return memo.changed_at.changed_since(revision);
|
||||||
}
|
}
|
||||||
if memo.value.is_some() {
|
if memo.value.is_some() {
|
||||||
// Otherwise, if we cache values, fall back to the full read to compute the result.
|
// Otherwise, if we cache values, fall back to the full read to compute the result.
|
||||||
drop(memo);
|
drop(memo);
|
||||||
drop(map_read);
|
drop(map_read);
|
||||||
return match self.read(db, key, descriptor) {
|
return match self.read(db, key, descriptor) {
|
||||||
Ok(v) => v.changed_at > revision,
|
Ok(v) => v.changed_at.changed_since(revision),
|
||||||
Err(CycleDetected) => true,
|
Err(CycleDetected) => true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -370,7 +365,8 @@ where
|
||||||
|
|
||||||
let mut map_write = self.map.write();
|
let mut map_write = self.map.write();
|
||||||
|
|
||||||
let changed_at = db.salsa_runtime().current_revision();
|
let current_revision = db.salsa_runtime().current_revision();
|
||||||
|
let changed_at = ChangedAt::Revision(current_revision);
|
||||||
|
|
||||||
map_write.insert(
|
map_write.insert(
|
||||||
key,
|
key,
|
||||||
|
@ -378,8 +374,28 @@ where
|
||||||
value: Some(value),
|
value: Some(value),
|
||||||
changed_at,
|
changed_at,
|
||||||
inputs: QueryDescriptorSet::new(),
|
inputs: QueryDescriptorSet::new(),
|
||||||
verified_at: changed_at,
|
verified_at: current_revision,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<DB, Q> Memo<DB, Q>
|
||||||
|
where
|
||||||
|
Q: QueryFunction<DB>,
|
||||||
|
DB: Database,
|
||||||
|
{
|
||||||
|
fn validate_memoized_value(&self, db: &DB) -> bool {
|
||||||
|
// If we don't have a memoized value, nothing to validate.
|
||||||
|
if !self.value.is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.changed_at {
|
||||||
|
ChangedAt::Revision(revision) => self
|
||||||
|
.inputs
|
||||||
|
.iter()
|
||||||
|
.all(|old_input| !old_input.maybe_changed_since(db, revision)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ where
|
||||||
/// - `descriptor`: the query whose result was read
|
/// - `descriptor`: the query whose result was read
|
||||||
/// - `changed_revision`: the last revision in which the result of that
|
/// - `changed_revision`: the last revision in which the result of that
|
||||||
/// query had changed
|
/// query had changed
|
||||||
crate fn report_query_read(&self, descriptor: &DB::QueryDescriptor, changed_at: Revision) {
|
crate fn report_query_read(&self, descriptor: &DB::QueryDescriptor, changed_at: ChangedAt) {
|
||||||
if let Some(top_query) = self.local_state.borrow_mut().query_stack.last_mut() {
|
if let Some(top_query) = self.local_state.borrow_mut().query_stack.last_mut() {
|
||||||
top_query.add_read(descriptor, changed_at);
|
top_query.add_read(descriptor, changed_at);
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ struct ActiveQuery<DB: Database> {
|
||||||
descriptor: DB::QueryDescriptor,
|
descriptor: DB::QueryDescriptor,
|
||||||
|
|
||||||
/// Records the maximum revision where any subquery changed
|
/// Records the maximum revision where any subquery changed
|
||||||
changed_at: Revision,
|
changed_at: ChangedAt,
|
||||||
|
|
||||||
/// Each subquery
|
/// Each subquery
|
||||||
subqueries: QueryDescriptorSet<DB>,
|
subqueries: QueryDescriptorSet<DB>,
|
||||||
|
@ -188,12 +188,12 @@ impl<DB: Database> ActiveQuery<DB> {
|
||||||
fn new(descriptor: DB::QueryDescriptor) -> Self {
|
fn new(descriptor: DB::QueryDescriptor) -> Self {
|
||||||
ActiveQuery {
|
ActiveQuery {
|
||||||
descriptor,
|
descriptor,
|
||||||
changed_at: Revision::ZERO,
|
changed_at: ChangedAt::Revision(Revision::ZERO),
|
||||||
subqueries: QueryDescriptorSet::new(),
|
subqueries: QueryDescriptorSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_read(&mut self, subquery: &DB::QueryDescriptor, changed_at: Revision) {
|
fn add_read(&mut self, subquery: &DB::QueryDescriptor, changed_at: ChangedAt) {
|
||||||
self.subqueries.insert(subquery.clone());
|
self.subqueries.insert(subquery.clone());
|
||||||
self.changed_at = self.changed_at.max(changed_at);
|
self.changed_at = self.changed_at.max(changed_at);
|
||||||
}
|
}
|
||||||
|
@ -214,6 +214,25 @@ impl std::fmt::Debug for Revision {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Records when a stamped value changed.
|
||||||
|
///
|
||||||
|
/// Note: the order of variants is significant. We sometimes use `max`
|
||||||
|
/// for example to find the "most recent revision" when something
|
||||||
|
/// changed.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum ChangedAt {
|
||||||
|
Revision(Revision),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChangedAt {
|
||||||
|
/// True if this value has changed after `revision`.
|
||||||
|
pub fn changed_since(self, revision: Revision) -> bool {
|
||||||
|
match self {
|
||||||
|
ChangedAt::Revision(r) => r > revision,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An insertion-order-preserving set of queries. Used to track the
|
/// An insertion-order-preserving set of queries. Used to track the
|
||||||
/// inputs accessed during query execution.
|
/// inputs accessed during query execution.
|
||||||
crate struct QueryDescriptorSet<DB: Database> {
|
crate struct QueryDescriptorSet<DB: Database> {
|
||||||
|
@ -249,5 +268,5 @@ impl<DB: Database> QueryDescriptorSet<DB> {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
crate struct StampedValue<V> {
|
crate struct StampedValue<V> {
|
||||||
crate value: V,
|
crate value: V,
|
||||||
crate changed_at: Revision,
|
crate changed_at: ChangedAt,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::runtime::ChangedAt;
|
||||||
use crate::runtime::Revision;
|
use crate::runtime::Revision;
|
||||||
use crate::runtime::StampedValue;
|
use crate::runtime::StampedValue;
|
||||||
use crate::CycleDetected;
|
use crate::CycleDetected;
|
||||||
|
@ -68,10 +69,9 @@ where
|
||||||
let was_in_progress = self.in_progress.lock().remove(key);
|
let was_in_progress = self.in_progress.lock().remove(key);
|
||||||
assert!(was_in_progress);
|
assert!(was_in_progress);
|
||||||
|
|
||||||
let revision_now = db.salsa_runtime().current_revision();
|
let changed_at = ChangedAt::Revision(db.salsa_runtime().current_revision());
|
||||||
|
|
||||||
db.salsa_runtime()
|
db.salsa_runtime().report_query_read(descriptor, changed_at);
|
||||||
.report_query_read(descriptor, revision_now);
|
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue