salsa/src/memoized.rs

96 lines
2.6 KiB
Rust
Raw Normal View History

2018-09-28 15:04:52 +00:00
use crate::BaseQueryContext;
use crate::CycleDetected;
use crate::Query;
use crate::QueryStorageOps;
use crate::QueryTable;
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;
// The master implementation that knits together all the queries
// contains a certain amount of boilerplate. This file aims to
// reduce that.
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>,
QC: BaseQueryContext,
{
map: RwLock<FxHashMap<Q::Key, QueryState<Q::Value>>>,
2018-09-28 15:04:52 +00:00
}
/// Defines the "current state" of query's memoized results.
#[derive(Debug)]
2018-09-28 21:47:40 +00:00
enum QueryState<V> {
/// 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.
Memoized(V),
}
2018-09-28 15:40:20 +00:00
impl<QC, Q> Default for MemoizedStorage<QC, Q>
where
Q: Query<QC>,
QC: BaseQueryContext,
{
fn default() -> Self {
MemoizedStorage {
map: RwLock::new(FxHashMap::default()),
2018-09-28 15:40:20 +00:00
}
}
}
2018-09-28 15:04:52 +00:00
impl<QC, Q> QueryStorageOps<QC, Q> for MemoizedStorage<QC, Q>
where
Q: Query<QC>,
QC: BaseQueryContext,
{
fn try_fetch<'q>(
&self,
query: &'q QC,
key: &Q::Key,
descriptor: impl FnOnce() -> QC::QueryDescriptor,
) -> Result<Q::Value, CycleDetected> {
{
let map_read = self.map.upgradable_read();
if let Some(value) = map_read.get(key) {
return match value {
QueryState::InProgress => Err(CycleDetected),
QueryState::Memoized(value) => Ok(value.clone()),
};
2018-09-28 15:04:52 +00:00
}
let mut map_write = RwLockUpgradableReadGuard::upgrade(map_read);
map_write.insert(key.clone(), QueryState::InProgress);
2018-09-28 15:04:52 +00:00
}
// If we get here, the query is in progress, and we are the
// ones tasked with finding its final value.
let descriptor = descriptor();
let value = query.execute_query_implementation::<Q>(descriptor, key);
{
let mut map_write = self.map.write();
let old_value = map_write.insert(key.clone(), QueryState::Memoized(value.clone()));
2018-09-28 15:04:52 +00:00
assert!(
match old_value {
Some(QueryState::InProgress) => true,
_ => false,
},
"expected in-progress state, not {:?}",
old_value
);
}
Ok(value)
}
}