mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-12 16:35:21 +00:00
add "input" storage
This commit is contained in:
parent
fd6e375553
commit
e2da42d36a
4 changed files with 174 additions and 11 deletions
123
src/input.rs
Normal file
123
src/input.rs
Normal 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,
|
||||
}
|
54
src/lib.rs
54
src/lib.rs
|
@ -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)*
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue