2018-09-29 12:14:58 +00:00
|
|
|
use crate::runtime::QueryDescriptorSet;
|
|
|
|
use crate::runtime::Revision;
|
2018-10-01 09:37:35 +00:00
|
|
|
use crate::runtime::StampedValue;
|
2018-09-28 15:04:52 +00:00
|
|
|
use crate::CycleDetected;
|
|
|
|
use crate::Query;
|
2018-09-29 09:34:29 +00:00
|
|
|
use crate::QueryContext;
|
2018-09-29 12:14:58 +00:00
|
|
|
use crate::QueryDescriptor;
|
2018-09-28 15:04:52 +00:00
|
|
|
use crate::QueryStorageOps;
|
|
|
|
use crate::QueryTable;
|
2018-09-30 11:28:22 +00:00
|
|
|
use log::debug;
|
2018-09-28 18:02:39 +00:00
|
|
|
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
2018-09-28 15:04:52 +00:00
|
|
|
use rustc_hash::FxHashMap;
|
|
|
|
use std::any::Any;
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
use std::fmt::Debug;
|
|
|
|
use std::fmt::Display;
|
|
|
|
use std::fmt::Write;
|
|
|
|
use std::hash::Hash;
|
|
|
|
|
2018-09-30 10:09:06 +00:00
|
|
|
/// Memoized queries store the result plus a list of the other queries
|
|
|
|
/// that they invoked. This means we can avoid recomputing them when
|
|
|
|
/// none of those inputs have changed.
|
2018-09-28 15:26:53 +00:00
|
|
|
pub struct MemoizedStorage<QC, Q>
|
2018-09-28 15:04:52 +00:00
|
|
|
where
|
|
|
|
Q: Query<QC>,
|
2018-09-29 09:34:29 +00:00
|
|
|
QC: QueryContext,
|
2018-09-28 15:04:52 +00:00
|
|
|
{
|
2018-09-29 12:14:58 +00:00
|
|
|
map: RwLock<FxHashMap<Q::Key, QueryState<QC, Q>>>,
|
2018-09-28 15:04:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-28 17:53:15 +00:00
|
|
|
/// Defines the "current state" of query's memoized results.
|
2018-09-29 12:14:58 +00:00
|
|
|
enum QueryState<QC, Q>
|
|
|
|
where
|
|
|
|
Q: Query<QC>,
|
|
|
|
QC: QueryContext,
|
|
|
|
{
|
2018-09-28 17:53:15 +00:00
|
|
|
/// We are currently computing the result of this query; if we see
|
|
|
|
/// this value in the table, it indeeds a cycle.
|
|
|
|
InProgress,
|
|
|
|
|
|
|
|
/// We have computed the query already, and here is the result.
|
2018-09-30 10:04:09 +00:00
|
|
|
Memoized(Memo<QC, Q>),
|
2018-09-29 11:24:53 +00:00
|
|
|
}
|
|
|
|
|
2018-09-30 10:04:09 +00:00
|
|
|
struct Memo<QC, Q>
|
2018-09-29 12:14:58 +00:00
|
|
|
where
|
|
|
|
Q: Query<QC>,
|
|
|
|
QC: QueryContext,
|
|
|
|
{
|
|
|
|
value: Q::Value,
|
|
|
|
|
|
|
|
inputs: QueryDescriptorSet<QC>,
|
|
|
|
|
|
|
|
/// Last time that we checked our inputs to see if they have
|
|
|
|
/// changed. If this is equal to the current revision, then the
|
|
|
|
/// value is up to date. If not, we need to check our inputs and
|
|
|
|
/// see if any of them have changed since our last check -- if so,
|
|
|
|
/// we'll need to re-execute.
|
|
|
|
verified_at: Revision,
|
|
|
|
|
|
|
|
/// Last time that our value changed.
|
|
|
|
changed_at: Revision,
|
2018-09-28 17:53:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-30 10:04:09 +00:00
|
|
|
impl<QC, Q> Memo<QC, Q>
|
|
|
|
where
|
|
|
|
Q: Query<QC>,
|
|
|
|
QC: QueryContext,
|
|
|
|
{
|
|
|
|
fn stamped_value(&self) -> StampedValue<Q::Value> {
|
|
|
|
StampedValue {
|
|
|
|
value: self.value.clone(),
|
|
|
|
changed_at: self.changed_at,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-28 15:40:20 +00:00
|
|
|
impl<QC, Q> Default for MemoizedStorage<QC, Q>
|
|
|
|
where
|
|
|
|
Q: Query<QC>,
|
2018-09-29 09:34:29 +00:00
|
|
|
QC: QueryContext,
|
2018-09-28 15:40:20 +00:00
|
|
|
{
|
|
|
|
fn default() -> Self {
|
|
|
|
MemoizedStorage {
|
2018-09-28 18:02:39 +00:00
|
|
|
map: RwLock::new(FxHashMap::default()),
|
2018-09-28 15:40:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-30 10:04:09 +00:00
|
|
|
impl<QC, Q> MemoizedStorage<QC, Q>
|
2018-09-28 15:04:52 +00:00
|
|
|
where
|
|
|
|
Q: Query<QC>,
|
2018-09-29 09:34:29 +00:00
|
|
|
QC: QueryContext,
|
2018-09-28 15:04:52 +00:00
|
|
|
{
|
2018-09-30 14:22:11 +00:00
|
|
|
fn read(
|
2018-09-28 15:04:52 +00:00
|
|
|
&self,
|
2018-09-30 14:22:11 +00:00
|
|
|
query: &QC,
|
2018-09-28 15:04:52 +00:00
|
|
|
key: &Q::Key,
|
2018-09-30 11:28:22 +00:00
|
|
|
descriptor: &QC::QueryDescriptor,
|
2018-09-30 10:04:09 +00:00
|
|
|
) -> Result<StampedValue<Q::Value>, CycleDetected> {
|
2018-09-29 12:14:58 +00:00
|
|
|
let revision_now = query.salsa_runtime().current_revision();
|
|
|
|
|
2018-09-30 11:28:22 +00:00
|
|
|
debug!(
|
|
|
|
"{:?}({:?}): invoked at {:?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
revision_now,
|
|
|
|
);
|
|
|
|
|
2018-09-29 12:14:58 +00:00
|
|
|
let mut old_value = {
|
2018-09-28 18:02:39 +00:00
|
|
|
let map_read = self.map.upgradable_read();
|
|
|
|
if let Some(value) = map_read.get(key) {
|
2018-09-29 12:14:58 +00:00
|
|
|
match value {
|
|
|
|
QueryState::InProgress => return Err(CycleDetected),
|
|
|
|
QueryState::Memoized(m) => {
|
2018-09-30 11:28:22 +00:00
|
|
|
debug!(
|
|
|
|
"{:?}({:?}): found memoized value verified_at={:?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
m.verified_at,
|
|
|
|
);
|
|
|
|
|
2018-09-29 12:14:58 +00:00
|
|
|
if m.verified_at == revision_now {
|
2018-09-30 11:28:22 +00:00
|
|
|
debug!(
|
|
|
|
"{:?}({:?}): returning memoized value (changed_at={:?})",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
m.changed_at,
|
|
|
|
);
|
|
|
|
|
2018-09-30 10:04:09 +00:00
|
|
|
return Ok(m.stamped_value());
|
2018-09-29 12:14:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-28 15:04:52 +00:00
|
|
|
}
|
2018-09-28 18:02:39 +00:00
|
|
|
|
|
|
|
let mut map_write = RwLockUpgradableReadGuard::upgrade(map_read);
|
2018-09-29 12:14:58 +00:00
|
|
|
map_write.insert(key.clone(), QueryState::InProgress)
|
|
|
|
};
|
|
|
|
|
|
|
|
// If we have an old-value, it *may* now be stale, since there
|
|
|
|
// has been a new revision since the last time we checked. So,
|
|
|
|
// first things first, let's walk over each of our previous
|
|
|
|
// inputs and check whether they are out of date.
|
|
|
|
if let Some(QueryState::Memoized(old_memo)) = &mut old_value {
|
|
|
|
if old_memo
|
|
|
|
.inputs
|
|
|
|
.iter()
|
2018-09-30 10:04:09 +00:00
|
|
|
.all(|old_input| !old_input.maybe_changed_since(query, old_memo.verified_at))
|
2018-09-29 12:14:58 +00:00
|
|
|
{
|
2018-09-30 11:28:22 +00:00
|
|
|
debug!("{:?}({:?}): inputs still valid", Q::default(), key);
|
|
|
|
|
2018-09-29 12:14:58 +00:00
|
|
|
// If none of out inputs have changed since the last time we refreshed
|
|
|
|
// 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;
|
2018-09-30 10:04:09 +00:00
|
|
|
let stamped_value = old_memo.stamped_value();
|
2018-09-29 12:14:58 +00:00
|
|
|
|
|
|
|
let mut map_write = self.map.write();
|
|
|
|
let placeholder = map_write.insert(key.clone(), old_value.unwrap());
|
|
|
|
assert!(
|
|
|
|
match placeholder {
|
|
|
|
Some(QueryState::InProgress) => true,
|
|
|
|
_ => false,
|
|
|
|
},
|
|
|
|
"expected in-progress state",
|
|
|
|
);
|
2018-09-30 10:04:09 +00:00
|
|
|
return Ok(stamped_value);
|
2018-09-29 12:14:58 +00:00
|
|
|
}
|
2018-09-28 15:04:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-29 12:14:58 +00:00
|
|
|
// Query was not previously executed or value is potentially
|
|
|
|
// stale. Let's execute!
|
|
|
|
let (value, inputs) = query
|
2018-09-29 09:31:26 +00:00
|
|
|
.salsa_runtime()
|
|
|
|
.execute_query_implementation::<Q>(query, descriptor, key);
|
2018-09-28 15:04:52 +00:00
|
|
|
|
2018-09-29 12:14:58 +00:00
|
|
|
// We assume that query is side-effect free -- that is, does
|
|
|
|
// not mutate the "inputs" to the query system. Sanity check
|
|
|
|
// that assumption here, at least to the best of our ability.
|
|
|
|
assert_eq!(
|
|
|
|
query.salsa_runtime().current_revision(),
|
|
|
|
revision_now,
|
|
|
|
"revision altered during query execution",
|
|
|
|
);
|
|
|
|
|
|
|
|
// If the new value is equal to the old one, then it didn't
|
|
|
|
// really change, even if some of its inputs have. So we can
|
|
|
|
// "backdate" our `changed_at` revision to be the same as the
|
|
|
|
// old value.
|
|
|
|
let mut changed_at = revision_now;
|
|
|
|
if let Some(QueryState::Memoized(old_memo)) = &old_value {
|
|
|
|
if old_memo.value == value {
|
|
|
|
changed_at = old_memo.changed_at;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-28 15:04:52 +00:00
|
|
|
{
|
2018-09-28 18:02:39 +00:00
|
|
|
let mut map_write = self.map.write();
|
2018-09-29 12:14:58 +00:00
|
|
|
|
2018-09-29 11:24:53 +00:00
|
|
|
let old_value = map_write.insert(
|
|
|
|
key.clone(),
|
2018-09-30 10:04:09 +00:00
|
|
|
QueryState::Memoized(Memo {
|
2018-09-29 11:24:53 +00:00
|
|
|
value: value.clone(),
|
2018-09-29 12:14:58 +00:00
|
|
|
inputs,
|
|
|
|
verified_at: revision_now,
|
|
|
|
changed_at,
|
2018-09-29 11:24:53 +00:00
|
|
|
}),
|
|
|
|
);
|
2018-09-28 15:04:52 +00:00
|
|
|
assert!(
|
|
|
|
match old_value {
|
|
|
|
Some(QueryState::InProgress) => true,
|
|
|
|
_ => false,
|
|
|
|
},
|
2018-09-29 12:14:58 +00:00
|
|
|
"expected in-progress state",
|
2018-09-28 15:04:52 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-09-30 10:04:09 +00:00
|
|
|
Ok(StampedValue { value, changed_at })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<QC, Q> QueryStorageOps<QC, Q> for MemoizedStorage<QC, Q>
|
|
|
|
where
|
|
|
|
Q: Query<QC>,
|
|
|
|
QC: QueryContext,
|
|
|
|
{
|
|
|
|
fn try_fetch<'q>(
|
|
|
|
&self,
|
|
|
|
query: &'q QC,
|
|
|
|
key: &Q::Key,
|
2018-09-30 11:28:22 +00:00
|
|
|
descriptor: &QC::QueryDescriptor,
|
2018-09-30 10:04:09 +00:00
|
|
|
) -> Result<Q::Value, CycleDetected> {
|
2018-09-30 11:28:22 +00:00
|
|
|
let StampedValue {
|
|
|
|
value,
|
|
|
|
changed_at: _,
|
|
|
|
} = self.read(query, key, &descriptor)?;
|
|
|
|
|
|
|
|
query.salsa_runtime().report_query_read(descriptor);
|
|
|
|
|
|
|
|
Ok(value)
|
2018-09-30 10:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn maybe_changed_since(
|
|
|
|
&self,
|
|
|
|
query: &'q QC,
|
|
|
|
revision: Revision,
|
|
|
|
key: &Q::Key,
|
|
|
|
descriptor: &QC::QueryDescriptor,
|
|
|
|
) -> bool {
|
2018-09-30 11:28:22 +00:00
|
|
|
let revision_now = query.salsa_runtime().current_revision();
|
|
|
|
|
|
|
|
debug!(
|
|
|
|
"{:?}({:?})::maybe_changed_since(revision={:?}, revision_now={:?})",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
revision,
|
|
|
|
revision_now,
|
|
|
|
);
|
|
|
|
|
2018-09-30 10:04:09 +00:00
|
|
|
// Check for the case where we have no cache entry, or our cache
|
|
|
|
// entry is up to date (common case):
|
|
|
|
{
|
|
|
|
let map_read = self.map.read();
|
|
|
|
match map_read.get(key) {
|
|
|
|
None | Some(QueryState::InProgress) => return true,
|
|
|
|
Some(QueryState::Memoized(memo)) => {
|
|
|
|
if memo.verified_at >= revision_now {
|
|
|
|
return memo.changed_at > revision;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise fall back to the full read to compute the result.
|
2018-09-30 11:28:22 +00:00
|
|
|
match self.read(query, key, descriptor) {
|
2018-09-30 10:04:09 +00:00
|
|
|
Ok(v) => v.changed_at > revision,
|
|
|
|
Err(CycleDetected) => true,
|
|
|
|
}
|
2018-09-28 15:04:52 +00:00
|
|
|
}
|
|
|
|
}
|