add "input" storage

This commit is contained in:
Niko Matsakis 2018-09-30 10:22:11 -04:00
parent fd6e375553
commit e2da42d36a
4 changed files with 174 additions and 11 deletions

123
src/input.rs Normal file
View file

@ -0,0 +1,123 @@
use crate::runtime::QueryDescriptorSet;
use crate::runtime::Revision;
use crate::CycleDetected;
use crate::Query;
use crate::QueryContext;
use crate::QueryDescriptor;
use crate::QueryStorageOps;
use crate::QueryTable;
use log::debug;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
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;
/// Input 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.
pub struct InputStorage<QC, Q>
where
Q: Query<QC>,
QC: QueryContext,
Q::Value: Default,
{
map: RwLock<FxHashMap<Q::Key, StampedValue<Q::Value>>>,
}
impl<QC, Q> Default for InputStorage<QC, Q>
where
Q: Query<QC>,
QC: QueryContext,
Q::Value: Default,
{
fn default() -> Self {
InputStorage {
map: RwLock::new(FxHashMap::default()),
}
}
}
impl<QC, Q> InputStorage<QC, Q>
where
Q: Query<QC>,
QC: QueryContext,
Q::Value: Default,
{
fn read<'q>(
&self,
_query: &'q QC,
key: &Q::Key,
_descriptor: &QC::QueryDescriptor,
) -> Result<StampedValue<Q::Value>, CycleDetected> {
{
let map_read = self.map.read();
if let Some(value) = map_read.get(key) {
return Ok(value.clone());
}
}
Ok(StampedValue {
value: <Q::Value>::default(),
changed_at: Revision::ZERO,
})
}
}
impl<QC, Q> QueryStorageOps<QC, Q> for InputStorage<QC, Q>
where
Q: Query<QC>,
QC: QueryContext,
Q::Value: Default,
{
fn try_fetch<'q>(
&self,
query: &'q QC,
key: &Q::Key,
descriptor: &QC::QueryDescriptor,
) -> Result<Q::Value, CycleDetected> {
let StampedValue {
value,
changed_at: _,
} = self.read(query, key, &descriptor)?;
query.salsa_runtime().report_query_read(descriptor);
Ok(value)
}
fn maybe_changed_since(
&self,
_query: &'q QC,
revision: Revision,
key: &Q::Key,
_descriptor: &QC::QueryDescriptor,
) -> bool {
debug!(
"{:?}({:?})::maybe_changed_since(revision={:?})",
Q::default(),
key,
revision,
);
let changed_at = {
let map_read = self.map.read();
map_read
.get(key)
.map(|v| v.changed_at)
.unwrap_or(Revision::ZERO)
};
changed_at > revision
}
}
#[derive(Clone)]
struct StampedValue<V> {
value: V,
changed_at: Revision,
}

View file

@ -16,6 +16,7 @@ use std::fmt::Display;
use std::fmt::Write;
use std::hash::Hash;
pub mod input;
pub mod memoized;
pub mod runtime;
pub mod volatile;
@ -72,9 +73,9 @@ where
/// Returns `Err` in the event of a cycle, meaning that computing
/// the value for this `key` is recursively attempting to fetch
/// itself.
fn try_fetch<'q>(
fn try_fetch(
&self,
query: &'q QC,
query: &QC,
key: &Q::Key,
descriptor: &QC::QueryDescriptor,
) -> Result<Q::Value, CycleDetected>;
@ -99,22 +100,33 @@ where
/// Other storage types will skip some or all of these steps.
fn maybe_changed_since(
&self,
query: &'q QC,
query: &QC,
revision: runtime::Revision,
key: &Q::Key,
descriptor: &QC::QueryDescriptor,
) -> bool;
}
/// An optional trait that is implemented for "user mutable" storage:
/// that is, storage whose value is not derived from other storage but
/// is set independently.
pub trait MutQueryStorageOps<QC, Q>: Default
where
QC: QueryContext,
Q: Query<QC>,
{
fn set(&self, query: &QC, key: &Q::Key, new_value: Q::Value);
}
#[derive(new)]
pub struct QueryTable<'me, QC, Q>
where
QC: QueryContext,
Q: Query<QC>,
{
pub query: &'me QC,
pub storage: &'me Q::Storage,
pub descriptor_fn: fn(&QC, &Q::Key) -> QC::QueryDescriptor,
query: &'me QC,
storage: &'me Q::Storage,
descriptor_fn: fn(&QC, &Q::Key) -> QC::QueryDescriptor,
}
pub struct CycleDetected;
@ -135,6 +147,14 @@ where
})
}
pub fn set(&self, key: Q::Key, value: Q::Value)
where
Q::Storage: MutQueryStorageOps<QC, Q>,
{
self.query.salsa_runtime().next_revision();
self.storage.set(self.query, &key, value);
}
fn descriptor(&self, key: &Q::Key) -> QC::QueryDescriptor {
(self.descriptor_fn)(self.query, key)
}
@ -261,6 +281,22 @@ macro_rules! query_definition {
}
};
(
@filter_attrs {
input { #[storage(input)] $($input:tt)* };
storage { $storage:tt };
other_attrs { $($other_attrs:tt)* };
}
) => {
$crate::query_definition! {
@filter_attrs {
input { $($input)* };
storage { input };
other_attrs { $($other_attrs)* };
}
}
};
(
@filter_attrs {
input { #[$attr:meta] $($input:tt)* };
@ -321,6 +357,12 @@ macro_rules! query_definition {
$crate::volatile::VolatileStorage
};
(
@storage_ty[$QC:ident, $Self:ident, input]
) => {
$crate::volatile::InputStorage<$QC, $Self>
};
// Various legal start states:
(
# $($tokens:tt)*

View file

@ -97,9 +97,9 @@ where
Q: Query<QC>,
QC: QueryContext,
{
fn read<'q>(
fn read(
&self,
query: &'q QC,
query: &QC,
key: &Q::Key,
descriptor: &QC::QueryDescriptor,
) -> Result<StampedValue<Q::Value>, CycleDetected> {

View file

@ -195,9 +195,7 @@ pub struct Revision {
}
impl Revision {
crate fn zero() -> Self {
Revision { generation: 0 }
}
crate const ZERO: Self = Revision { generation: 0 };
}
impl std::fmt::Debug for Revision {