mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-13 00:40:22 +00:00
merge volatile and memoized queries
This commit is contained in:
parent
2d6e454638
commit
5ad0049b9f
3 changed files with 46 additions and 107 deletions
|
@ -19,7 +19,6 @@ use std::hash::Hash;
|
|||
pub mod input;
|
||||
pub mod memoized;
|
||||
pub mod runtime;
|
||||
pub mod volatile;
|
||||
|
||||
/// The base trait which your "query context" must implement. Gives
|
||||
/// access to the salsa runtime, which you must embed into your query
|
||||
|
@ -428,7 +427,7 @@ macro_rules! query_group {
|
|||
(
|
||||
@storage_ty[$DB:ident, $Self:ident, volatile]
|
||||
) => {
|
||||
$crate::volatile::VolatileStorage<$DB, $Self>
|
||||
$crate::memoized::VolatileStorage<$DB, $Self>
|
||||
};
|
||||
|
||||
(
|
||||
|
|
|
@ -31,6 +31,11 @@ pub type MemoizedStorage<DB, Q> = WeakMemoizedStorage<DB, Q, AlwaysMemoizeValue>
|
|||
/// storage requirements.
|
||||
pub type DependencyStorage<DB, Q> = WeakMemoizedStorage<DB, Q, NeverMemoizeValue>;
|
||||
|
||||
/// "Dependency" queries just track their dependencies and not the
|
||||
/// actual value (which they produce on demand). This lessens the
|
||||
/// storage requirements.
|
||||
pub type VolatileStorage<DB, Q> = WeakMemoizedStorage<DB, Q, VolatileValue>;
|
||||
|
||||
pub struct WeakMemoizedStorage<DB, Q, MP>
|
||||
where
|
||||
Q: QueryFunction<DB>,
|
||||
|
@ -47,6 +52,8 @@ where
|
|||
DB: Database,
|
||||
{
|
||||
fn should_memoize_value(key: &Q::Key) -> bool;
|
||||
|
||||
fn is_volatile(key: &Q::Key) -> bool;
|
||||
}
|
||||
|
||||
pub enum AlwaysMemoizeValue {}
|
||||
|
@ -58,6 +65,10 @@ where
|
|||
fn should_memoize_value(_key: &Q::Key) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn is_volatile(_key: &Q::Key) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub enum NeverMemoizeValue {}
|
||||
|
@ -69,6 +80,25 @@ where
|
|||
fn should_memoize_value(_key: &Q::Key) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_volatile(_key: &Q::Key) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub enum VolatileValue {}
|
||||
impl<DB, Q> MemoizationPolicy<DB, Q> for VolatileValue
|
||||
where
|
||||
Q: QueryFunction<DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn should_memoize_value(_key: &Q::Key) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_volatile(_key: &Q::Key) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the "current state" of query's memoized results.
|
||||
|
@ -97,6 +127,7 @@ where
|
|||
/// The result of the query, if we decide to memoize it.
|
||||
value: Option<Q::Value>,
|
||||
|
||||
/// The inputs that went into our query, if we are tracking them.
|
||||
inputs: QueryDescriptorSet<DB>,
|
||||
|
||||
/// Last time that we checked our inputs to see if they have
|
||||
|
@ -202,12 +233,16 @@ where
|
|||
|
||||
// Query was not previously executed, or value is potentially
|
||||
// stale, or value is absent. Let's execute!
|
||||
let (mut stamped_value, inputs) =
|
||||
db.salsa_runtime()
|
||||
.execute_query_implementation(descriptor, || {
|
||||
debug!("{:?}({:?}): executing query", Q::default(), key);
|
||||
Q::execute(db, key.clone())
|
||||
});
|
||||
let runtime = db.salsa_runtime();
|
||||
let (mut stamped_value, inputs) = runtime.execute_query_implementation(descriptor, || {
|
||||
debug!("{:?}({:?}): executing query", Q::default(), key);
|
||||
|
||||
if self.is_volatile(key) {
|
||||
runtime.report_untracked_read();
|
||||
}
|
||||
|
||||
Q::execute(db, key.clone())
|
||||
});
|
||||
|
||||
// We assume that query is side-effect free -- that is, does
|
||||
// not mutate the "inputs" to the query system. Sanity check
|
||||
|
@ -270,6 +305,10 @@ where
|
|||
fn should_memoize_value(&self, key: &Q::Key) -> bool {
|
||||
MP::should_memoize_value(key)
|
||||
}
|
||||
|
||||
fn is_volatile(&self, key: &Q::Key) -> bool {
|
||||
MP::is_volatile(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, Q, MP> QueryStorageOps<DB, Q> for WeakMemoizedStorage<DB, Q, MP>
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
use crate::runtime::ChangedAt;
|
||||
use crate::runtime::QueryDescriptorSet;
|
||||
use crate::runtime::Revision;
|
||||
use crate::runtime::StampedValue;
|
||||
use crate::CycleDetected;
|
||||
use crate::Database;
|
||||
use crate::QueryFunction;
|
||||
use crate::QueryStorageOps;
|
||||
use crate::QueryTable;
|
||||
use log::debug;
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashSet;
|
||||
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;
|
||||
|
||||
/// Volatile Storage is just **always** considered dirty. Any time you
|
||||
/// ask for the result of such a query, it is recomputed.
|
||||
pub struct VolatileStorage<DB, Q>
|
||||
where
|
||||
Q: QueryFunction<DB>,
|
||||
DB: Database,
|
||||
{
|
||||
/// We don't store the results of volatile queries,
|
||||
/// but we track in-progress set to detect cycles.
|
||||
in_progress: Mutex<FxHashSet<Q::Key>>,
|
||||
}
|
||||
|
||||
impl<DB, Q> Default for VolatileStorage<DB, Q>
|
||||
where
|
||||
Q: QueryFunction<DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn default() -> Self {
|
||||
VolatileStorage {
|
||||
in_progress: Mutex::new(FxHashSet::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, Q> QueryStorageOps<DB, Q> for VolatileStorage<DB, Q>
|
||||
where
|
||||
Q: QueryFunction<DB>,
|
||||
DB: Database,
|
||||
{
|
||||
fn try_fetch<'q>(
|
||||
&self,
|
||||
db: &'q DB,
|
||||
key: &Q::Key,
|
||||
descriptor: &DB::QueryDescriptor,
|
||||
) -> Result<Q::Value, CycleDetected> {
|
||||
if !self.in_progress.lock().insert(key.clone()) {
|
||||
return Err(CycleDetected);
|
||||
}
|
||||
|
||||
let runtime = db.salsa_runtime();
|
||||
|
||||
let (StampedValue { value, changed_at }, inputs) =
|
||||
runtime.execute_query_implementation(descriptor, || {
|
||||
debug!("{:?}({:?}): executing query", Q::default(), key);
|
||||
runtime.report_untracked_read();
|
||||
Q::execute(db, key.clone())
|
||||
});
|
||||
|
||||
assert!(changed_at == ChangedAt::Revision(db.salsa_runtime().current_revision()));
|
||||
assert!(match inputs {
|
||||
QueryDescriptorSet::Untracked => true,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
let was_in_progress = self.in_progress.lock().remove(key);
|
||||
assert!(was_in_progress);
|
||||
|
||||
db.salsa_runtime().report_query_read(descriptor, changed_at);
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn maybe_changed_since(
|
||||
&self,
|
||||
_db: &'q DB,
|
||||
revision: Revision,
|
||||
key: &Q::Key,
|
||||
_descriptor: &DB::QueryDescriptor,
|
||||
) -> bool {
|
||||
debug!(
|
||||
"{:?}({:?})::maybe_changed_since(revision={:?}) ==> true (volatile)",
|
||||
Q::default(),
|
||||
key,
|
||||
revision,
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue