2019-01-22 20:33:22 +00:00
|
|
|
use crate::debug::TableEntry;
|
2018-10-16 08:20:49 +00:00
|
|
|
use crate::plumbing::CycleDetected;
|
2019-01-24 11:33:02 +00:00
|
|
|
use crate::plumbing::DatabaseKey;
|
2018-10-16 08:20:49 +00:00
|
|
|
use crate::plumbing::QueryFunction;
|
2018-10-23 21:36:47 +00:00
|
|
|
use crate::plumbing::QueryStorageMassOps;
|
2018-10-16 08:20:49 +00:00
|
|
|
use crate::plumbing::QueryStorageOps;
|
2018-10-09 12:53:13 +00:00
|
|
|
use crate::runtime::ChangedAt;
|
2018-10-22 13:52:49 +00:00
|
|
|
use crate::runtime::FxIndexSet;
|
2018-09-29 12:14:58 +00:00
|
|
|
use crate::runtime::Revision;
|
2018-10-12 16:10:25 +00:00
|
|
|
use crate::runtime::Runtime;
|
|
|
|
use crate::runtime::RuntimeId;
|
2018-10-01 09:37:35 +00:00
|
|
|
use crate::runtime::StampedValue;
|
2019-01-27 14:14:57 +00:00
|
|
|
use crate::{Database, DiscardIf, DiscardWhat, Event, EventKind, SweepStrategy};
|
2018-10-22 13:52:49 +00:00
|
|
|
use log::{debug, info};
|
2018-10-13 10:04:57 +00:00
|
|
|
use parking_lot::Mutex;
|
2018-10-30 20:30:12 +00:00
|
|
|
use parking_lot::RwLock;
|
2018-09-28 15:04:52 +00:00
|
|
|
use rustc_hash::FxHashMap;
|
2018-10-15 12:28:55 +00:00
|
|
|
use smallvec::SmallVec;
|
2018-10-06 17:13:30 +00:00
|
|
|
use std::marker::PhantomData;
|
2018-10-13 09:45:57 +00:00
|
|
|
use std::ops::Deref;
|
2018-10-15 15:48:21 +00:00
|
|
|
use std::sync::mpsc::{self, Receiver, Sender};
|
2018-10-22 13:52:49 +00:00
|
|
|
use std::sync::Arc;
|
2018-09-28 15:04:52 +00:00
|
|
|
|
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-10-09 16:15:33 +00:00
|
|
|
pub type MemoizedStorage<DB, Q> = DerivedStorage<DB, Q, AlwaysMemoizeValue>;
|
2018-10-06 17:13:30 +00:00
|
|
|
|
|
|
|
/// "Dependency" queries just track their dependencies and not the
|
|
|
|
/// actual value (which they produce on demand). This lessens the
|
|
|
|
/// storage requirements.
|
2018-10-09 16:15:33 +00:00
|
|
|
pub type DependencyStorage<DB, Q> = DerivedStorage<DB, Q, NeverMemoizeValue>;
|
2018-10-06 17:13:30 +00:00
|
|
|
|
2018-10-09 16:14:09 +00:00
|
|
|
/// "Dependency" queries just track their dependencies and not the
|
|
|
|
/// actual value (which they produce on demand). This lessens the
|
|
|
|
/// storage requirements.
|
2018-10-09 16:15:33 +00:00
|
|
|
pub type VolatileStorage<DB, Q> = DerivedStorage<DB, Q, VolatileValue>;
|
2018-10-09 16:14:09 +00:00
|
|
|
|
2018-10-09 16:15:33 +00:00
|
|
|
/// Handles storage where the value is 'derived' by executing a
|
|
|
|
/// function (in contrast to "inputs").
|
|
|
|
pub struct DerivedStorage<DB, Q, MP>
|
2018-09-28 15:04:52 +00:00
|
|
|
where
|
2018-10-05 08:59:10 +00:00
|
|
|
Q: QueryFunction<DB>,
|
2018-10-05 08:54:51 +00:00
|
|
|
DB: Database,
|
2018-10-09 12:39:41 +00:00
|
|
|
MP: MemoizationPolicy<DB, Q>,
|
2018-09-28 15:04:52 +00:00
|
|
|
{
|
2018-10-05 08:54:51 +00:00
|
|
|
map: RwLock<FxHashMap<Q::Key, QueryState<DB, Q>>>,
|
2018-10-09 12:39:41 +00:00
|
|
|
policy: PhantomData<MP>,
|
2018-10-06 17:13:30 +00:00
|
|
|
}
|
|
|
|
|
2019-01-11 07:04:09 +00:00
|
|
|
impl<DB, Q, MP> std::panic::RefUnwindSafe for DerivedStorage<DB, Q, MP>
|
|
|
|
where
|
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
DB: Database,
|
|
|
|
MP: MemoizationPolicy<DB, Q>,
|
|
|
|
Q::Key: std::panic::RefUnwindSafe,
|
|
|
|
Q::Value: std::panic::RefUnwindSafe,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-10-09 12:39:41 +00:00
|
|
|
pub trait MemoizationPolicy<DB, Q>
|
2018-10-06 17:13:30 +00:00
|
|
|
where
|
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
DB: Database,
|
|
|
|
{
|
|
|
|
fn should_memoize_value(key: &Q::Key) -> bool;
|
2018-10-09 16:14:09 +00:00
|
|
|
|
2018-10-18 22:05:28 +00:00
|
|
|
fn memoized_value_eq(old_value: &Q::Value, new_value: &Q::Value) -> bool;
|
|
|
|
|
2018-10-09 17:28:33 +00:00
|
|
|
fn should_track_inputs(key: &Q::Key) -> bool;
|
2018-10-06 17:13:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum AlwaysMemoizeValue {}
|
2018-10-09 12:39:41 +00:00
|
|
|
impl<DB, Q> MemoizationPolicy<DB, Q> for AlwaysMemoizeValue
|
2018-10-06 17:13:30 +00:00
|
|
|
where
|
|
|
|
Q: QueryFunction<DB>,
|
2018-10-18 22:05:28 +00:00
|
|
|
Q::Value: Eq,
|
2018-10-06 17:13:30 +00:00
|
|
|
DB: Database,
|
|
|
|
{
|
|
|
|
fn should_memoize_value(_key: &Q::Key) -> bool {
|
|
|
|
true
|
|
|
|
}
|
2018-10-09 16:14:09 +00:00
|
|
|
|
2018-10-18 22:05:28 +00:00
|
|
|
fn memoized_value_eq(old_value: &Q::Value, new_value: &Q::Value) -> bool {
|
|
|
|
old_value == new_value
|
|
|
|
}
|
|
|
|
|
2018-10-09 17:28:33 +00:00
|
|
|
fn should_track_inputs(_key: &Q::Key) -> bool {
|
|
|
|
true
|
2018-10-09 16:14:09 +00:00
|
|
|
}
|
2018-10-06 17:13:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum NeverMemoizeValue {}
|
2018-10-09 12:39:41 +00:00
|
|
|
impl<DB, Q> MemoizationPolicy<DB, Q> for NeverMemoizeValue
|
2018-10-06 17:13:30 +00:00
|
|
|
where
|
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
DB: Database,
|
|
|
|
{
|
|
|
|
fn should_memoize_value(_key: &Q::Key) -> bool {
|
|
|
|
false
|
|
|
|
}
|
2018-10-09 16:14:09 +00:00
|
|
|
|
2018-10-18 22:05:28 +00:00
|
|
|
fn memoized_value_eq(_old_value: &Q::Value, _new_value: &Q::Value) -> bool {
|
|
|
|
panic!("cannot reach since we never memoize")
|
|
|
|
}
|
|
|
|
|
2018-10-09 17:28:33 +00:00
|
|
|
fn should_track_inputs(_key: &Q::Key) -> bool {
|
|
|
|
true
|
2018-10-09 16:14:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2018-10-09 16:18:16 +00:00
|
|
|
// Why memoize? Well, if the "volatile" value really is
|
|
|
|
// constantly changing, we still want to capture its value
|
|
|
|
// until the next revision is triggered and ensure it doesn't
|
|
|
|
// change -- otherwise the system gets into an inconsistent
|
|
|
|
// state where the same query reports back different values.
|
|
|
|
true
|
2018-10-09 16:14:09 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 22:05:28 +00:00
|
|
|
fn memoized_value_eq(_old_value: &Q::Value, _new_value: &Q::Value) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2018-10-09 17:28:33 +00:00
|
|
|
fn should_track_inputs(_key: &Q::Key) -> bool {
|
|
|
|
false
|
2018-10-09 16:14:09 +00:00
|
|
|
}
|
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-10-05 08:54:51 +00:00
|
|
|
enum QueryState<DB, Q>
|
2018-09-29 12:14:58 +00:00
|
|
|
where
|
2018-10-05 08:59:10 +00:00
|
|
|
Q: QueryFunction<DB>,
|
2018-10-05 08:54:51 +00:00
|
|
|
DB: Database,
|
2018-09-29 12:14:58 +00:00
|
|
|
{
|
2018-10-12 16:10:25 +00:00
|
|
|
/// The runtime with the given id is currently computing the
|
|
|
|
/// result of this query; if we see this value in the table, it
|
|
|
|
/// indeeds a cycle.
|
2018-10-13 12:38:42 +00:00
|
|
|
InProgress {
|
|
|
|
id: RuntimeId,
|
2018-10-15 12:28:55 +00:00
|
|
|
waiting: Mutex<SmallVec<[Sender<StampedValue<Q::Value>>; 2]>>,
|
2018-10-13 12:38:42 +00:00
|
|
|
},
|
2018-09-28 17:53:15 +00:00
|
|
|
|
|
|
|
/// We have computed the query already, and here is the result.
|
2018-10-05 08:54:51 +00:00
|
|
|
Memoized(Memo<DB, Q>),
|
2018-09-29 11:24:53 +00:00
|
|
|
}
|
|
|
|
|
2018-10-13 12:38:42 +00:00
|
|
|
impl<DB, Q> QueryState<DB, Q>
|
|
|
|
where
|
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
DB: Database,
|
|
|
|
{
|
|
|
|
fn in_progress(id: RuntimeId) -> Self {
|
|
|
|
QueryState::InProgress {
|
|
|
|
id,
|
2018-10-15 12:28:55 +00:00
|
|
|
waiting: Default::default(),
|
2018-10-13 12:38:42 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-22 20:33:22 +00:00
|
|
|
|
|
|
|
fn value(&self) -> Option<Q::Value> {
|
|
|
|
match self {
|
|
|
|
QueryState::InProgress { .. } => None,
|
|
|
|
QueryState::Memoized(memo) => memo.value.clone(),
|
|
|
|
}
|
|
|
|
}
|
2018-10-13 12:38:42 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
struct Memo<DB, Q>
|
2018-09-29 12:14:58 +00:00
|
|
|
where
|
2018-10-05 08:59:10 +00:00
|
|
|
Q: QueryFunction<DB>,
|
2018-10-05 08:54:51 +00:00
|
|
|
DB: Database,
|
2018-09-29 12:14:58 +00:00
|
|
|
{
|
2018-10-06 17:13:30 +00:00
|
|
|
/// The result of the query, if we decide to memoize it.
|
|
|
|
value: Option<Q::Value>,
|
2018-09-29 12:14:58 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
/// Last revision when this memo was verified (if there are
|
|
|
|
/// untracked inputs, this will also be when the memo was
|
|
|
|
/// created).
|
|
|
|
verified_at: Revision,
|
|
|
|
|
|
|
|
/// Last revision when the memoized value was observed to change.
|
|
|
|
changed_at: Revision,
|
|
|
|
|
2018-10-09 16:14:09 +00:00
|
|
|
/// The inputs that went into our query, if we are tracking them.
|
2018-10-22 13:52:49 +00:00
|
|
|
inputs: MemoInputs<DB>,
|
|
|
|
}
|
2018-09-29 12:14:58 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
/// An insertion-order-preserving set of queries. Used to track the
|
|
|
|
/// inputs accessed during query execution.
|
|
|
|
pub(crate) enum MemoInputs<DB: Database> {
|
|
|
|
// No inputs
|
|
|
|
Constant,
|
|
|
|
|
|
|
|
// Non-empty set of inputs fully known
|
|
|
|
Tracked {
|
2019-01-24 11:33:02 +00:00
|
|
|
inputs: Arc<FxIndexSet<DB::DatabaseKey>>,
|
2018-10-22 13:52:49 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Unknown quantity of inputs
|
|
|
|
Untracked,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<DB: Database> MemoInputs<DB> {
|
|
|
|
fn is_constant(&self) -> bool {
|
|
|
|
if let MemoInputs::Constant = self {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<DB: Database> std::fmt::Debug for MemoInputs<DB> {
|
|
|
|
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
MemoInputs::Constant => fmt.debug_struct("Constant").finish(),
|
|
|
|
MemoInputs::Tracked { inputs } => {
|
|
|
|
fmt.debug_struct("Tracked").field("inputs", inputs).finish()
|
|
|
|
}
|
|
|
|
MemoInputs::Untracked => fmt.debug_struct("Untracked").finish(),
|
|
|
|
}
|
|
|
|
}
|
2018-09-30 10:04:09 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 16:15:33 +00:00
|
|
|
impl<DB, Q, MP> Default for DerivedStorage<DB, Q, MP>
|
2018-09-28 15:40:20 +00:00
|
|
|
where
|
2018-10-05 08:59:10 +00:00
|
|
|
Q: QueryFunction<DB>,
|
2018-10-05 08:54:51 +00:00
|
|
|
DB: Database,
|
2018-10-09 12:39:41 +00:00
|
|
|
MP: MemoizationPolicy<DB, Q>,
|
2018-09-28 15:40:20 +00:00
|
|
|
{
|
|
|
|
fn default() -> Self {
|
2018-10-09 16:15:33 +00:00
|
|
|
DerivedStorage {
|
2018-09-28 18:02:39 +00:00
|
|
|
map: RwLock::new(FxHashMap::default()),
|
2018-10-09 12:39:41 +00:00
|
|
|
policy: PhantomData,
|
2018-09-28 15:40:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-14 11:23:38 +00:00
|
|
|
/// Return value of `probe` helper.
|
2018-10-13 10:27:41 +00:00
|
|
|
enum ProbeState<V, G> {
|
2018-10-15 13:31:25 +00:00
|
|
|
UpToDate(Result<V, CycleDetected>),
|
2018-10-13 10:27:41 +00:00
|
|
|
StaleOrAbsent(G),
|
|
|
|
}
|
|
|
|
|
2018-10-09 16:15:33 +00:00
|
|
|
impl<DB, Q, MP> DerivedStorage<DB, Q, MP>
|
2018-09-28 15:04:52 +00:00
|
|
|
where
|
2018-10-05 08:59:10 +00:00
|
|
|
Q: QueryFunction<DB>,
|
2018-10-05 08:54:51 +00:00
|
|
|
DB: Database,
|
2018-10-09 12:39:41 +00:00
|
|
|
MP: MemoizationPolicy<DB, Q>,
|
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-10-05 08:54:51 +00:00
|
|
|
db: &DB,
|
2018-09-28 15:04:52 +00:00
|
|
|
key: &Q::Key,
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: &DB::DatabaseKey,
|
2018-09-30 10:04:09 +00:00
|
|
|
) -> Result<StampedValue<Q::Value>, CycleDetected> {
|
2018-10-11 20:48:20 +00:00
|
|
|
let runtime = db.salsa_runtime();
|
|
|
|
|
2018-10-31 17:30:10 +00:00
|
|
|
// NB: We don't need to worry about people modifying the
|
|
|
|
// revision out from under our feet. Either `db` is a frozen
|
|
|
|
// database, in which case there is a lock, or the mutator
|
|
|
|
// thread is the current thread, and it will be prevented from
|
|
|
|
// doing any `set` invocations while the query function runs.
|
2018-10-11 20:48:20 +00:00
|
|
|
let revision_now = runtime.current_revision();
|
2018-09-29 12:14:58 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
info!(
|
2018-09-30 11:28:22 +00:00
|
|
|
"{:?}({:?}): invoked at {:?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
revision_now,
|
|
|
|
);
|
|
|
|
|
2018-10-15 12:28:55 +00:00
|
|
|
// First, do a check with a read-lock.
|
2019-01-24 11:33:02 +00:00
|
|
|
match self.probe(
|
|
|
|
db,
|
|
|
|
self.map.read(),
|
|
|
|
runtime,
|
|
|
|
revision_now,
|
|
|
|
database_key,
|
|
|
|
key,
|
|
|
|
) {
|
2018-10-15 13:31:25 +00:00
|
|
|
ProbeState::UpToDate(v) => return v,
|
2018-10-15 12:28:55 +00:00
|
|
|
ProbeState::StaleOrAbsent(_guard) => (),
|
|
|
|
}
|
2018-10-09 12:37:57 +00:00
|
|
|
|
2019-01-24 11:33:02 +00:00
|
|
|
self.read_upgrade(db, key, database_key, revision_now)
|
2018-10-15 12:40:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Second phase of a read operation: acquires an upgradable-read
|
|
|
|
/// and -- if needed -- validates whether inputs have changed,
|
|
|
|
/// recomputes value, etc. This is invoked after our initial probe
|
|
|
|
/// shows a potentially out of date value.
|
|
|
|
fn read_upgrade(
|
|
|
|
&self,
|
|
|
|
db: &DB,
|
|
|
|
key: &Q::Key,
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: &DB::DatabaseKey,
|
2018-10-15 12:40:44 +00:00
|
|
|
revision_now: Revision,
|
|
|
|
) -> Result<StampedValue<Q::Value>, CycleDetected> {
|
|
|
|
let runtime = db.salsa_runtime();
|
|
|
|
|
2019-01-04 13:39:59 +00:00
|
|
|
debug!(
|
|
|
|
"{:?}({:?}): read_upgrade(revision_now={:?})",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
revision_now,
|
|
|
|
);
|
|
|
|
|
2018-10-15 12:40:44 +00:00
|
|
|
// Check with an upgradable read to see if there is a value
|
|
|
|
// already. (This permits other readers but prevents anyone
|
|
|
|
// else from running `read_upgrade` at the same time.)
|
2018-10-30 20:30:12 +00:00
|
|
|
//
|
|
|
|
// FIXME(Amanieu/parking_lot#101) -- we are using a write-lock
|
|
|
|
// and not an upgradable read here because upgradable reads
|
|
|
|
// can sometimes encounter deadlocks.
|
2019-01-24 11:33:02 +00:00
|
|
|
let mut old_memo = match self.probe(
|
|
|
|
db,
|
|
|
|
self.map.write(),
|
|
|
|
runtime,
|
|
|
|
revision_now,
|
|
|
|
database_key,
|
|
|
|
key,
|
|
|
|
) {
|
|
|
|
ProbeState::UpToDate(v) => return v,
|
|
|
|
ProbeState::StaleOrAbsent(mut map) => {
|
|
|
|
match map.insert(key.clone(), QueryState::in_progress(runtime.id())) {
|
|
|
|
Some(QueryState::Memoized(old_memo)) => Some(old_memo),
|
|
|
|
Some(QueryState::InProgress { .. }) => unreachable!(),
|
|
|
|
None => None,
|
2018-10-16 09:48:01 +00:00
|
|
|
}
|
2019-01-24 11:33:02 +00:00
|
|
|
}
|
|
|
|
};
|
2018-09-29 12:14:58 +00:00
|
|
|
|
2019-01-24 11:33:02 +00:00
|
|
|
let panic_guard = PanicGuard::new(&self.map, key, database_key, runtime);
|
2018-10-23 04:59:12 +00:00
|
|
|
|
2018-09-29 12:14:58 +00:00
|
|
|
// 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.
|
2018-10-16 09:48:01 +00:00
|
|
|
if let Some(memo) = &mut old_memo {
|
2018-10-22 13:52:49 +00:00
|
|
|
if let Some(value) = memo.validate_memoized_value(db, revision_now) {
|
|
|
|
info!(
|
|
|
|
"{:?}({:?}): validated old memoized value",
|
|
|
|
Q::default(),
|
|
|
|
key
|
|
|
|
);
|
|
|
|
|
2018-10-31 00:18:56 +00:00
|
|
|
db.salsa_event(|| Event {
|
2018-10-31 00:39:56 +00:00
|
|
|
runtime_id: runtime.id(),
|
2018-10-31 00:18:56 +00:00
|
|
|
kind: EventKind::DidValidateMemoizedValue {
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: database_key.clone(),
|
2018-10-31 00:18:56 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2018-11-01 22:17:36 +00:00
|
|
|
panic_guard.proceed(old_memo.unwrap(), &value);
|
2018-10-31 00:18:56 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
return Ok(value);
|
2018-09-29 12:14:58 +00:00
|
|
|
}
|
2018-09-28 15:04:52 +00:00
|
|
|
}
|
|
|
|
|
2018-10-06 17:13:30 +00:00
|
|
|
// Query was not previously executed, or value is potentially
|
|
|
|
// stale, or value is absent. Let's execute!
|
2019-01-24 11:33:02 +00:00
|
|
|
let mut result = runtime.execute_query_implementation(db, database_key, || {
|
2018-10-22 13:52:49 +00:00
|
|
|
info!("{:?}({:?}): executing query", Q::default(), key);
|
2018-10-09 16:14:09 +00:00
|
|
|
|
2018-10-09 17:28:33 +00:00
|
|
|
if !self.should_track_inputs(key) {
|
2018-10-09 16:14:09 +00:00
|
|
|
runtime.report_untracked_read();
|
|
|
|
}
|
|
|
|
|
|
|
|
Q::execute(db, key.clone())
|
|
|
|
});
|
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!(
|
2018-10-11 20:48:20 +00:00
|
|
|
runtime.current_revision(),
|
2018-09-29 12:14:58 +00:00
|
|
|
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
|
2018-10-01 09:46:07 +00:00
|
|
|
// "backdate" its `changed_at` revision to be the same as the
|
2018-09-29 12:14:58 +00:00
|
|
|
// old value.
|
2018-10-16 09:48:01 +00:00
|
|
|
if let Some(old_memo) = &old_memo {
|
2018-10-18 22:05:28 +00:00
|
|
|
if let Some(old_value) = &old_memo.value {
|
2018-10-22 13:52:49 +00:00
|
|
|
if MP::memoized_value_eq(&old_value, &result.value) {
|
2019-01-04 13:39:59 +00:00
|
|
|
debug!(
|
|
|
|
"read_upgrade({:?}({:?})): value is equal, back-dating to {:?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
old_memo.changed_at,
|
|
|
|
);
|
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
assert!(old_memo.changed_at <= result.changed_at.revision);
|
|
|
|
result.changed_at.revision = old_memo.changed_at;
|
2018-10-18 22:05:28 +00:00
|
|
|
}
|
2018-09-29 12:14:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
let new_value = StampedValue {
|
|
|
|
value: result.value,
|
|
|
|
changed_at: result.changed_at,
|
|
|
|
};
|
|
|
|
|
2018-11-01 22:17:36 +00:00
|
|
|
let value = if self.should_memoize_value(key) {
|
|
|
|
Some(new_value.value.clone())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2019-01-04 13:39:59 +00:00
|
|
|
debug!(
|
|
|
|
"read_upgrade({:?}({:?})): result.changed_at={:?}, result.subqueries = {:#?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
result.changed_at,
|
|
|
|
result.subqueries,
|
|
|
|
);
|
|
|
|
|
2018-11-01 22:17:36 +00:00
|
|
|
let inputs = match result.subqueries {
|
|
|
|
None => MemoInputs::Untracked,
|
|
|
|
|
2019-01-24 11:33:02 +00:00
|
|
|
Some(database_keys) => {
|
2018-11-01 22:17:36 +00:00
|
|
|
// If all things that we read were constants, then
|
|
|
|
// we don't need to track our inputs: our value
|
|
|
|
// can never be invalidated.
|
|
|
|
//
|
|
|
|
// If OTOH we read at least *some* non-constant
|
|
|
|
// inputs, then we do track our inputs (even the
|
|
|
|
// constants), so that if we run the GC, we know
|
|
|
|
// which constants we looked at.
|
2019-01-24 11:33:02 +00:00
|
|
|
if database_keys.is_empty() || result.changed_at.is_constant {
|
2018-11-01 22:17:36 +00:00
|
|
|
MemoInputs::Constant
|
|
|
|
} else {
|
|
|
|
MemoInputs::Tracked {
|
2019-01-24 11:33:02 +00:00
|
|
|
inputs: Arc::new(database_keys),
|
2018-10-22 13:52:49 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-01 22:17:36 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
panic_guard.proceed(
|
|
|
|
Memo {
|
|
|
|
value,
|
|
|
|
changed_at: result.changed_at.revision,
|
|
|
|
verified_at: revision_now,
|
|
|
|
inputs,
|
|
|
|
},
|
|
|
|
&new_value,
|
|
|
|
);
|
2018-09-28 15:04:52 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
Ok(new_value)
|
2018-09-30 10:04:09 +00:00
|
|
|
}
|
2018-10-01 10:02:00 +00:00
|
|
|
|
2018-10-13 09:45:57 +00:00
|
|
|
/// Helper for `read`:
|
|
|
|
///
|
2018-10-14 11:23:38 +00:00
|
|
|
/// Invoked with the guard `map` of some lock on `self.map` (read
|
2018-10-15 13:30:08 +00:00
|
|
|
/// or write) as well as details about the key to look up. Looks
|
|
|
|
/// in the map to see if we have an up-to-date value or a
|
|
|
|
/// cycle. Returns a suitable `ProbeState`:
|
2018-10-14 11:23:38 +00:00
|
|
|
///
|
2018-10-15 13:30:08 +00:00
|
|
|
/// - `ProbeState::UpToDate(r)` if the table has an up-to-date
|
|
|
|
/// value (or we blocked on another thread that produced such a value).
|
2018-10-14 11:23:38 +00:00
|
|
|
/// - `ProbeState::CycleDetected` if this thread is (directly or
|
|
|
|
/// indirectly) already computing this value.
|
|
|
|
/// - `ProbeState::BlockedOnOtherThread` if some other thread
|
|
|
|
/// (which does not depend on us) was already computing this
|
|
|
|
/// value; caller should re-acquire the lock and try again.
|
2018-10-15 13:30:08 +00:00
|
|
|
/// - `ProbeState::StaleOrAbsent` if either (a) there is no memo
|
|
|
|
/// for this key, (b) the memo has no value; or (c) the memo
|
|
|
|
/// has not been verified at the current revision.
|
2018-10-14 11:23:38 +00:00
|
|
|
///
|
|
|
|
/// Note that in all cases **except** for `StaleOrAbsent`, the lock on
|
|
|
|
/// `map` will have been released.
|
2018-10-15 13:30:08 +00:00
|
|
|
fn probe<MapGuard>(
|
2018-10-13 09:45:57 +00:00
|
|
|
&self,
|
2018-10-31 00:18:56 +00:00
|
|
|
db: &DB,
|
2018-10-13 09:45:57 +00:00
|
|
|
map: MapGuard,
|
|
|
|
runtime: &Runtime<DB>,
|
|
|
|
revision_now: Revision,
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: &DB::DatabaseKey,
|
2018-10-13 09:45:57 +00:00
|
|
|
key: &Q::Key,
|
2018-10-15 13:30:08 +00:00
|
|
|
) -> ProbeState<StampedValue<Q::Value>, MapGuard>
|
2018-10-13 09:45:57 +00:00
|
|
|
where
|
|
|
|
MapGuard: Deref<Target = FxHashMap<Q::Key, QueryState<DB, Q>>>,
|
|
|
|
{
|
|
|
|
match map.get(key) {
|
2018-10-15 12:28:55 +00:00
|
|
|
Some(QueryState::InProgress { id, waiting }) => {
|
2018-10-14 11:23:38 +00:00
|
|
|
let other_id = *id;
|
2019-01-24 11:33:02 +00:00
|
|
|
return match self.register_with_in_progress_thread(
|
|
|
|
runtime,
|
|
|
|
database_key,
|
|
|
|
other_id,
|
|
|
|
waiting,
|
|
|
|
) {
|
2018-10-15 15:48:21 +00:00
|
|
|
Ok(rx) => {
|
|
|
|
// Release our lock on `self.map`, so other thread
|
|
|
|
// can complete.
|
|
|
|
std::mem::drop(map);
|
|
|
|
|
2018-10-31 00:18:56 +00:00
|
|
|
db.salsa_event(|| Event {
|
2018-10-31 00:39:56 +00:00
|
|
|
runtime_id: db.salsa_runtime().id(),
|
2018-10-31 00:18:56 +00:00
|
|
|
kind: EventKind::WillBlockOn {
|
2018-10-31 00:39:56 +00:00
|
|
|
other_runtime_id: other_id,
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: database_key.clone(),
|
2018-10-31 00:18:56 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2019-01-10 08:57:24 +00:00
|
|
|
let value = rx.recv().unwrap_or_else(|_| db.on_propagated_panic());
|
2018-10-15 15:48:21 +00:00
|
|
|
ProbeState::UpToDate(Ok(value))
|
2018-10-13 12:38:42 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 15:48:21 +00:00
|
|
|
Err(CycleDetected) => ProbeState::UpToDate(Err(CycleDetected)),
|
|
|
|
};
|
2018-10-13 09:45:57 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 13:30:08 +00:00
|
|
|
Some(QueryState::Memoized(memo)) => {
|
2018-10-22 13:52:49 +00:00
|
|
|
debug!("{:?}({:?}): found memoized value", Q::default(), key);
|
2018-10-13 09:45:57 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
if let Some(value) = memo.probe_memoized_value(revision_now) {
|
|
|
|
info!(
|
|
|
|
"{:?}({:?}): returning memoized value changed at {:?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
value.changed_at
|
|
|
|
);
|
|
|
|
|
|
|
|
return ProbeState::UpToDate(Ok(value));
|
2018-10-13 09:45:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
2018-10-13 10:27:41 +00:00
|
|
|
ProbeState::StaleOrAbsent(map)
|
2018-10-13 09:45:57 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 15:48:21 +00:00
|
|
|
/// Helper:
|
|
|
|
///
|
|
|
|
/// When we encounter an `InProgress` indicator, we need to either
|
|
|
|
/// report a cycle or else register ourselves to be notified when
|
|
|
|
/// that work completes. This helper does that; it returns a port
|
|
|
|
/// where you can wait for the final value that wound up being
|
|
|
|
/// computed (but first drop the lock on the map).
|
|
|
|
fn register_with_in_progress_thread(
|
|
|
|
&self,
|
|
|
|
runtime: &Runtime<DB>,
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: &DB::DatabaseKey,
|
2018-10-15 15:48:21 +00:00
|
|
|
other_id: RuntimeId,
|
|
|
|
waiting: &Mutex<SmallVec<[Sender<StampedValue<Q::Value>>; 2]>>,
|
|
|
|
) -> Result<Receiver<StampedValue<Q::Value>>, CycleDetected> {
|
|
|
|
if other_id == runtime.id() {
|
|
|
|
return Err(CycleDetected);
|
|
|
|
} else {
|
2019-01-24 11:33:02 +00:00
|
|
|
if !runtime.try_block_on(database_key, other_id) {
|
2018-10-15 15:48:21 +00:00
|
|
|
return Err(CycleDetected);
|
|
|
|
}
|
|
|
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
|
|
|
|
|
|
|
// The reader of this will have to acquire map
|
|
|
|
// lock, we don't need any particular ordering.
|
|
|
|
waiting.lock().push(tx);
|
|
|
|
|
|
|
|
Ok(rx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-06 17:13:30 +00:00
|
|
|
fn should_memoize_value(&self, key: &Q::Key) -> bool {
|
2018-10-09 12:39:41 +00:00
|
|
|
MP::should_memoize_value(key)
|
2018-10-06 17:13:30 +00:00
|
|
|
}
|
2018-10-09 16:14:09 +00:00
|
|
|
|
2018-10-09 17:28:33 +00:00
|
|
|
fn should_track_inputs(&self, key: &Q::Key) -> bool {
|
|
|
|
MP::should_track_inputs(key)
|
2018-10-09 16:14:09 +00:00
|
|
|
}
|
2018-09-30 10:04:09 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 04:59:12 +00:00
|
|
|
struct PanicGuard<'db, DB, Q>
|
|
|
|
where
|
2018-11-01 22:17:36 +00:00
|
|
|
DB: Database,
|
2018-10-23 04:59:12 +00:00
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
{
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: &'db DB::DatabaseKey,
|
2018-10-23 04:59:12 +00:00
|
|
|
key: &'db Q::Key,
|
2018-11-01 22:17:36 +00:00
|
|
|
map: &'db RwLock<FxHashMap<Q::Key, QueryState<DB, Q>>>,
|
|
|
|
runtime: &'db Runtime<DB>,
|
2018-10-23 04:59:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'db, DB, Q> PanicGuard<'db, DB, Q>
|
|
|
|
where
|
|
|
|
DB: Database + 'db,
|
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
{
|
2018-10-30 09:06:43 +00:00
|
|
|
fn new(
|
|
|
|
map: &'db RwLock<FxHashMap<Q::Key, QueryState<DB, Q>>>,
|
|
|
|
key: &'db Q::Key,
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: &'db DB::DatabaseKey,
|
2018-11-01 22:17:36 +00:00
|
|
|
runtime: &'db Runtime<DB>,
|
2018-10-30 09:06:43 +00:00
|
|
|
) -> Self {
|
2018-11-01 22:17:36 +00:00
|
|
|
Self {
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key,
|
2018-11-01 22:17:36 +00:00
|
|
|
key,
|
|
|
|
map,
|
|
|
|
runtime,
|
|
|
|
}
|
2018-10-23 04:59:12 +00:00
|
|
|
}
|
2018-10-29 12:53:11 +00:00
|
|
|
|
2018-11-01 22:17:36 +00:00
|
|
|
/// Proceed with our panic guard by overwriting the placeholder for `key`.
|
|
|
|
/// Once that completes, ensure that our deconstructor is not run once we
|
|
|
|
/// are out of scope.
|
|
|
|
fn proceed(self, memo: Memo<DB, Q>, new_value: &StampedValue<Q::Value>) {
|
|
|
|
self.overwrite_placeholder(Some(memo), Some(new_value));
|
2018-10-29 12:53:11 +00:00
|
|
|
std::mem::forget(self)
|
|
|
|
}
|
2018-11-01 22:17:36 +00:00
|
|
|
|
|
|
|
/// Overwrites the `InProgress` placeholder for `key` that we
|
|
|
|
/// inserted; if others were blocked, waiting for us to finish,
|
|
|
|
/// then notify them.
|
|
|
|
fn overwrite_placeholder(
|
|
|
|
&self,
|
|
|
|
memo: Option<Memo<DB, Q>>,
|
|
|
|
new_value: Option<&StampedValue<Q::Value>>,
|
|
|
|
) {
|
|
|
|
let mut write = self.map.write();
|
|
|
|
|
|
|
|
let old_value = match memo {
|
|
|
|
// Replace the `InProgress` marker that we installed with the new
|
|
|
|
// memo, thus releasing our unique access to this key.
|
|
|
|
Some(memo) => write.insert(self.key.clone(), QueryState::Memoized(memo)),
|
|
|
|
|
|
|
|
// We had installed an `InProgress` marker, but we panicked before
|
|
|
|
// it could be removed. At this point, we therefore "own" unique
|
|
|
|
// access to our slot, so we can just remove the key.
|
|
|
|
None => write.remove(self.key),
|
|
|
|
};
|
|
|
|
|
|
|
|
match old_value {
|
|
|
|
Some(QueryState::InProgress { id, waiting }) => {
|
|
|
|
assert_eq!(id, self.runtime.id());
|
|
|
|
|
|
|
|
self.runtime
|
2019-01-24 11:33:02 +00:00
|
|
|
.unblock_queries_blocked_on_self(self.database_key);
|
2018-11-01 22:17:36 +00:00
|
|
|
|
|
|
|
match new_value {
|
|
|
|
// If anybody has installed themselves in our "waiting"
|
|
|
|
// list, notify them that the value is available.
|
|
|
|
Some(new_value) => {
|
|
|
|
for tx in waiting.into_inner() {
|
|
|
|
tx.send(new_value.clone()).unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have no value to send when we are panicking.
|
|
|
|
// Therefore, we need to drop the sending half of the
|
|
|
|
// channel so that our panic propagates to those waiting
|
|
|
|
// on the receiving half.
|
|
|
|
None => std::mem::drop(waiting),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => panic!(
|
|
|
|
"\
|
|
|
|
Unexpected panic during query evaluation, aborting the process.
|
|
|
|
|
|
|
|
Please report this bug to https://github.com/salsa-rs/salsa/issues."
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
2018-10-23 04:59:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'db, DB, Q> Drop for PanicGuard<'db, DB, Q>
|
|
|
|
where
|
|
|
|
DB: Database + 'db,
|
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
{
|
|
|
|
fn drop(&mut self) {
|
2018-10-29 12:53:11 +00:00
|
|
|
if std::thread::panicking() {
|
2018-11-01 22:17:36 +00:00
|
|
|
// We panicked before we could proceed and need to remove `key`.
|
|
|
|
self.overwrite_placeholder(None, None)
|
2018-10-29 12:53:11 +00:00
|
|
|
} else {
|
2018-10-30 09:06:43 +00:00
|
|
|
// If no panic occurred, then panic guard ought to be
|
|
|
|
// "forgotten" and so this Drop code should never run.
|
2018-10-29 12:53:11 +00:00
|
|
|
panic!(".forget() was not called")
|
|
|
|
}
|
2018-10-23 04:59:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-09 16:15:33 +00:00
|
|
|
impl<DB, Q, MP> QueryStorageOps<DB, Q> for DerivedStorage<DB, Q, MP>
|
2018-09-30 10:04:09 +00:00
|
|
|
where
|
2018-10-05 08:59:10 +00:00
|
|
|
Q: QueryFunction<DB>,
|
2018-10-05 08:54:51 +00:00
|
|
|
DB: Database,
|
2018-10-09 12:39:41 +00:00
|
|
|
MP: MemoizationPolicy<DB, Q>,
|
2018-09-30 10:04:09 +00:00
|
|
|
{
|
2018-10-09 21:44:26 +00:00
|
|
|
fn try_fetch(
|
2018-09-30 10:04:09 +00:00
|
|
|
&self,
|
2018-10-09 21:44:26 +00:00
|
|
|
db: &DB,
|
2018-09-30 10:04:09 +00:00
|
|
|
key: &Q::Key,
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: &DB::DatabaseKey,
|
2018-09-30 10:04:09 +00:00
|
|
|
) -> Result<Q::Value, CycleDetected> {
|
2019-01-24 11:33:02 +00:00
|
|
|
let StampedValue { value, changed_at } = self.read(db, key, &database_key)?;
|
2018-09-30 11:28:22 +00:00
|
|
|
|
2019-01-24 11:33:02 +00:00
|
|
|
db.salsa_runtime()
|
|
|
|
.report_query_read(database_key, changed_at);
|
2018-09-30 11:28:22 +00:00
|
|
|
|
|
|
|
Ok(value)
|
2018-09-30 10:04:09 +00:00
|
|
|
}
|
|
|
|
|
2018-10-09 21:44:26 +00:00
|
|
|
fn maybe_changed_since(
|
2018-09-30 10:04:09 +00:00
|
|
|
&self,
|
2018-10-09 21:44:26 +00:00
|
|
|
db: &DB,
|
2018-09-30 10:04:09 +00:00
|
|
|
revision: Revision,
|
|
|
|
key: &Q::Key,
|
2019-01-24 11:33:02 +00:00
|
|
|
database_key: &DB::DatabaseKey,
|
2018-09-30 10:04:09 +00:00
|
|
|
) -> bool {
|
2018-10-12 16:10:25 +00:00
|
|
|
let runtime = db.salsa_runtime();
|
|
|
|
let revision_now = runtime.current_revision();
|
2018-09-30 11:28:22 +00:00
|
|
|
|
|
|
|
debug!(
|
2019-01-04 13:39:59 +00:00
|
|
|
"maybe_changed_since({:?}({:?})) called with revision={:?}, revision_now={:?}",
|
2018-09-30 11:28:22 +00:00
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
revision,
|
|
|
|
revision_now,
|
|
|
|
);
|
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
// Acquire read lock to start. In some of the arms below, we
|
|
|
|
// drop this explicitly.
|
|
|
|
let map = self.map.read();
|
2018-10-15 15:55:01 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
// Look for a memoized value.
|
|
|
|
let memo = match map.get(key) {
|
|
|
|
// If somebody depends on us, but we have no map
|
|
|
|
// entry, that must mean that it was found to be out
|
|
|
|
// of date and removed.
|
2019-01-04 13:39:59 +00:00
|
|
|
None => {
|
|
|
|
debug!(
|
|
|
|
"maybe_changed_since({:?}({:?}): no value",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
);
|
|
|
|
return true;
|
|
|
|
}
|
2018-10-15 15:55:01 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
// This value is being actively recomputed. Wait for
|
|
|
|
// that thread to finish (assuming it's not dependent
|
|
|
|
// on us...) and check its associated revision.
|
|
|
|
Some(QueryState::InProgress { id, waiting }) => {
|
|
|
|
let other_id = *id;
|
2019-01-04 13:39:59 +00:00
|
|
|
debug!(
|
|
|
|
"maybe_changed_since({:?}({:?}): blocking on thread `{:?}`",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
other_id,
|
|
|
|
);
|
2019-01-24 11:33:02 +00:00
|
|
|
match self.register_with_in_progress_thread(
|
|
|
|
runtime,
|
|
|
|
database_key,
|
|
|
|
other_id,
|
|
|
|
waiting,
|
|
|
|
) {
|
2018-10-22 13:52:49 +00:00
|
|
|
Ok(rx) => {
|
|
|
|
// Release our lock on `self.map`, so other thread
|
|
|
|
// can complete.
|
|
|
|
std::mem::drop(map);
|
2018-10-15 09:29:21 +00:00
|
|
|
|
2019-01-10 08:57:24 +00:00
|
|
|
let value = rx.recv().unwrap_or_else(|_| db.on_propagated_panic());
|
2018-10-22 13:52:49 +00:00
|
|
|
return value.changed_at.changed_since(revision);
|
2018-10-15 09:29:21 +00:00
|
|
|
}
|
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
// Consider a cycle to have changed.
|
|
|
|
Err(CycleDetected) => return true,
|
|
|
|
}
|
|
|
|
}
|
2018-10-15 09:29:21 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
Some(QueryState::Memoized(memo)) => memo,
|
|
|
|
};
|
|
|
|
|
|
|
|
if memo.verified_at == revision_now {
|
2019-01-04 13:39:59 +00:00
|
|
|
debug!(
|
|
|
|
"maybe_changed_since({:?}({:?}): {:?} since up-to-date memo that changed at {:?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
memo.changed_at > revision,
|
|
|
|
memo.changed_at,
|
|
|
|
);
|
2018-10-22 13:52:49 +00:00
|
|
|
return memo.changed_at > revision;
|
|
|
|
}
|
|
|
|
|
|
|
|
let inputs = match &memo.inputs {
|
|
|
|
MemoInputs::Untracked => {
|
|
|
|
// we don't know the full set of
|
|
|
|
// inputs, so if there is a new
|
|
|
|
// revision, we must assume it is
|
|
|
|
// dirty
|
2019-01-04 13:39:59 +00:00
|
|
|
debug!(
|
|
|
|
"maybe_changed_since({:?}({:?}): true since untracked inputs",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
);
|
2018-10-22 13:52:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
MemoInputs::Constant => None,
|
|
|
|
|
|
|
|
MemoInputs::Tracked { inputs } => {
|
|
|
|
// At this point, the value may be dirty (we have
|
2019-01-24 11:33:02 +00:00
|
|
|
// to check the database-keys). If we have a cached
|
2018-10-22 13:52:49 +00:00
|
|
|
// value, we'll just fall back to invoking `read`,
|
|
|
|
// which will do that checking (and a bit more) --
|
|
|
|
// note that we skip the "pure read" part as we
|
|
|
|
// already know the result.
|
|
|
|
assert!(inputs.len() > 0);
|
|
|
|
if memo.value.is_some() {
|
|
|
|
std::mem::drop(map);
|
2019-01-24 11:33:02 +00:00
|
|
|
return match self.read_upgrade(db, key, database_key, revision_now) {
|
2019-01-04 13:39:59 +00:00
|
|
|
Ok(v) => {
|
|
|
|
debug!(
|
|
|
|
"maybe_changed_since({:?}({:?}): {:?} since (recomputed) value changed at {:?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
v.changed_at.changed_since(revision),
|
|
|
|
v.changed_at,
|
|
|
|
);
|
|
|
|
v.changed_at.changed_since(revision)
|
|
|
|
}
|
2018-10-22 13:52:49 +00:00
|
|
|
Err(CycleDetected) => true,
|
|
|
|
};
|
2018-09-30 10:04:09 +00:00
|
|
|
}
|
2018-10-22 13:52:49 +00:00
|
|
|
|
|
|
|
Some(inputs.clone())
|
2018-10-15 09:29:21 +00:00
|
|
|
}
|
2018-10-06 17:13:30 +00:00
|
|
|
};
|
2018-09-30 10:04:09 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
// We have a **tracked set of inputs**
|
2019-01-24 11:33:02 +00:00
|
|
|
// (found in `database_keys`) that need to
|
2018-10-22 13:52:49 +00:00
|
|
|
// be validated.
|
|
|
|
std::mem::drop(map);
|
|
|
|
|
|
|
|
// Iterate the inputs and see if any have maybe changed.
|
|
|
|
let maybe_changed = inputs
|
2018-10-15 09:29:21 +00:00
|
|
|
.iter()
|
2018-10-22 13:52:49 +00:00
|
|
|
.flat_map(|inputs| inputs.iter())
|
2019-03-22 08:58:47 +00:00
|
|
|
.filter(|input| input.maybe_changed_since(db, revision))
|
2018-10-22 13:52:49 +00:00
|
|
|
.inspect(|input| {
|
2018-10-15 09:29:21 +00:00
|
|
|
debug!(
|
|
|
|
"{:?}({:?}): input `{:?}` may have changed",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
2018-10-22 13:52:49 +00:00
|
|
|
input
|
2018-10-15 09:29:21 +00:00
|
|
|
)
|
2018-11-01 04:19:13 +00:00
|
|
|
})
|
|
|
|
.next()
|
2018-10-15 09:29:21 +00:00
|
|
|
.is_some();
|
2018-10-06 17:13:30 +00:00
|
|
|
|
2018-10-15 09:29:21 +00:00
|
|
|
// Either way, we have to update our entry.
|
2018-10-30 09:06:43 +00:00
|
|
|
//
|
|
|
|
// Keep in mind, though, we only acquired a read lock so a lot
|
|
|
|
// could have happened in the interim. =) Therefore, we have
|
|
|
|
// to probe the current state of `key` and in some cases we
|
|
|
|
// ought to do nothing.
|
2018-10-15 09:29:21 +00:00
|
|
|
{
|
|
|
|
let mut map = self.map.write();
|
2018-10-30 09:06:43 +00:00
|
|
|
match map.get_mut(key) {
|
|
|
|
Some(QueryState::Memoized(memo)) => {
|
|
|
|
if memo.verified_at == revision_now {
|
|
|
|
// Since we started verifying inputs, somebody
|
|
|
|
// else has come along and updated this value
|
|
|
|
// (they may even have recomputed
|
|
|
|
// it). Therefore, we should not touch this
|
|
|
|
// memo.
|
|
|
|
//
|
|
|
|
// FIXME: Should we still return whatever
|
|
|
|
// `maybe_changed` value we computed,
|
|
|
|
// however..? It seems .. harmless to indicate
|
|
|
|
// that the value has changed, but possibly
|
|
|
|
// less efficient? (It may cause some
|
|
|
|
// downstream value to be recomputed that
|
|
|
|
// wouldn't otherwise have to be?)
|
|
|
|
} else if maybe_changed {
|
|
|
|
// We found this entry is out of date and
|
|
|
|
// nobody touch it in the meantime. Just
|
|
|
|
// remove it.
|
|
|
|
map.remove(key);
|
|
|
|
} else {
|
|
|
|
// We found this entry is valid. Update the
|
|
|
|
// `verified_at` to reflect the current
|
|
|
|
// revision.
|
2018-10-15 09:29:21 +00:00
|
|
|
memo.verified_at = revision_now;
|
|
|
|
}
|
2018-10-30 09:06:43 +00:00
|
|
|
}
|
2018-10-06 17:13:30 +00:00
|
|
|
|
2018-10-30 09:06:43 +00:00
|
|
|
Some(QueryState::InProgress { .. }) => {
|
|
|
|
// Since we started verifying inputs, somebody
|
|
|
|
// else has come along and started updated this
|
|
|
|
// value. Just leave their marker alone and return
|
|
|
|
// whatever `maybe_changed` value we computed.
|
|
|
|
}
|
2018-10-23 21:36:47 +00:00
|
|
|
|
2018-10-30 09:06:43 +00:00
|
|
|
None => {
|
|
|
|
// Since we started verifying inputs, somebody
|
2018-10-30 17:03:56 +00:00
|
|
|
// else has come along and removed this value. The
|
|
|
|
// GC can do this, for example. That's fine.
|
2018-10-15 09:29:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-06 17:13:30 +00:00
|
|
|
|
2018-10-15 09:29:21 +00:00
|
|
|
maybe_changed
|
2018-09-28 15:04:52 +00:00
|
|
|
}
|
2018-10-11 08:37:29 +00:00
|
|
|
|
|
|
|
fn is_constant(&self, _db: &DB, key: &Q::Key) -> bool {
|
|
|
|
let map_read = self.map.read();
|
|
|
|
match map_read.get(key) {
|
|
|
|
None => false,
|
2018-10-13 10:08:29 +00:00
|
|
|
Some(QueryState::InProgress { .. }) => panic!("query in progress"),
|
2018-10-22 13:52:49 +00:00
|
|
|
Some(QueryState::Memoized(memo)) => memo.inputs.is_constant(),
|
2018-10-11 08:37:29 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-24 08:38:30 +00:00
|
|
|
|
2019-01-22 20:33:22 +00:00
|
|
|
fn entries<C>(&self, _db: &DB) -> C
|
2018-10-24 08:38:30 +00:00
|
|
|
where
|
2019-01-22 20:33:22 +00:00
|
|
|
C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>,
|
2018-10-24 08:38:30 +00:00
|
|
|
{
|
|
|
|
let map = self.map.read();
|
2019-01-22 20:33:22 +00:00
|
|
|
map.iter()
|
|
|
|
.map(|(key, query_state)| TableEntry::new(key.clone(), query_state.value()))
|
|
|
|
.collect()
|
2018-10-24 08:38:30 +00:00
|
|
|
}
|
2018-09-28 15:04:52 +00:00
|
|
|
}
|
2018-10-05 19:23:05 +00:00
|
|
|
|
2018-10-23 21:36:47 +00:00
|
|
|
impl<DB, Q, MP> QueryStorageMassOps<DB> for DerivedStorage<DB, Q, MP>
|
|
|
|
where
|
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
DB: Database,
|
|
|
|
MP: MemoizationPolicy<DB, Q>,
|
|
|
|
{
|
2018-10-25 09:47:25 +00:00
|
|
|
fn sweep(&self, db: &DB, strategy: SweepStrategy) {
|
2018-10-23 21:36:47 +00:00
|
|
|
let mut map_write = self.map.write();
|
2018-10-24 12:43:53 +00:00
|
|
|
let revision_now = db.salsa_runtime().current_revision();
|
2018-10-23 21:36:47 +00:00
|
|
|
map_write.retain(|key, query_state| {
|
|
|
|
match query_state {
|
2018-10-30 09:06:43 +00:00
|
|
|
// Leave stuff that is currently being computed -- the
|
|
|
|
// other thread doing that work has unique access to
|
|
|
|
// this slot and we should not interfere.
|
2018-10-23 21:36:47 +00:00
|
|
|
QueryState::InProgress { .. } => {
|
|
|
|
debug!("sweep({:?}({:?})): in-progress", Q::default(), key);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2019-01-27 14:14:57 +00:00
|
|
|
// Otherwise, drop only value or the whole memo accoring to the
|
|
|
|
// strategy.
|
2018-10-23 21:36:47 +00:00
|
|
|
QueryState::Memoized(memo) => {
|
|
|
|
debug!(
|
|
|
|
"sweep({:?}({:?})): last verified at {:?}, current revision {:?}",
|
|
|
|
Q::default(),
|
|
|
|
key,
|
|
|
|
memo.verified_at,
|
|
|
|
revision_now
|
|
|
|
);
|
2018-10-24 12:43:53 +00:00
|
|
|
|
2019-03-22 20:24:37 +00:00
|
|
|
// Check if this memo read something "untracked"
|
|
|
|
// -- meaning non-deterministic. In this case, we
|
|
|
|
// can only collect "outdated" data that wasn't
|
|
|
|
// used in the current revision. This is because
|
|
|
|
// if we collected something from the current
|
|
|
|
// revision, we might wind up re-executing the
|
|
|
|
// query later in the revision and getting a
|
|
|
|
// distinct result.
|
|
|
|
let is_volatile = match memo.inputs {
|
|
|
|
MemoInputs::Untracked => true,
|
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
|
2018-10-24 12:43:53 +00:00
|
|
|
// Since we don't acquire a query lock in this
|
|
|
|
// method, it *is* possible for the revision to
|
|
|
|
// change while we are executing. However, it is
|
|
|
|
// *not* possible for any memos to have been
|
|
|
|
// written into this table that reflect the new
|
|
|
|
// revision, since we are holding the write lock
|
|
|
|
// when we read `revision_now`.
|
|
|
|
assert!(memo.verified_at <= revision_now);
|
2019-01-27 14:14:57 +00:00
|
|
|
match strategy.discard_if {
|
2019-01-29 16:41:15 +00:00
|
|
|
DiscardIf::Never => unreachable!(),
|
2019-03-22 20:24:37 +00:00
|
|
|
|
|
|
|
// If we are only discarding outdated things,
|
|
|
|
// and this is not outdated, keep it.
|
2019-01-27 14:14:57 +00:00
|
|
|
DiscardIf::Outdated if memo.verified_at == revision_now => true,
|
2019-03-22 20:24:37 +00:00
|
|
|
|
|
|
|
// As explained on the `is_volatile` variable
|
|
|
|
// definition, if this is a volatile entry, we
|
|
|
|
// can't discard it unless it is outdated.
|
|
|
|
DiscardIf::Always if is_volatile && memo.verified_at == revision_now => {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, we can discard -- discard whatever the user requested.
|
2019-01-27 14:14:57 +00:00
|
|
|
DiscardIf::Outdated | DiscardIf::Always => match strategy.discard_what {
|
2019-01-29 16:41:15 +00:00
|
|
|
DiscardWhat::Nothing => unreachable!(),
|
2019-01-27 14:14:57 +00:00
|
|
|
DiscardWhat::Values => {
|
|
|
|
memo.value = None;
|
|
|
|
true
|
|
|
|
}
|
|
|
|
DiscardWhat::Everything => false,
|
|
|
|
},
|
2018-10-25 09:47:25 +00:00
|
|
|
}
|
2018-10-23 21:36:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-09 12:53:13 +00:00
|
|
|
impl<DB, Q> Memo<DB, Q>
|
|
|
|
where
|
|
|
|
Q: QueryFunction<DB>,
|
|
|
|
DB: Database,
|
|
|
|
{
|
2018-10-22 13:52:49 +00:00
|
|
|
fn validate_memoized_value(
|
|
|
|
&mut self,
|
|
|
|
db: &DB,
|
|
|
|
revision_now: Revision,
|
|
|
|
) -> Option<StampedValue<Q::Value>> {
|
2018-10-09 12:53:13 +00:00
|
|
|
// If we don't have a memoized value, nothing to validate.
|
2018-10-22 13:52:49 +00:00
|
|
|
let value = self.value.as_ref()?;
|
2018-10-09 12:53:13 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
assert!(self.verified_at != revision_now);
|
|
|
|
let verified_at = self.verified_at;
|
2018-10-09 13:27:59 +00:00
|
|
|
|
2019-01-04 13:39:59 +00:00
|
|
|
debug!(
|
|
|
|
"validate_memoized_value({:?}): verified_at={:#?}",
|
|
|
|
Q::default(),
|
|
|
|
self.inputs,
|
|
|
|
);
|
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
let is_constant = match &mut self.inputs {
|
|
|
|
// We can't validate values that had untracked inputs; just have to
|
|
|
|
// re-execute.
|
|
|
|
MemoInputs::Untracked { .. } => {
|
|
|
|
return None;
|
2018-10-09 20:20:57 +00:00
|
|
|
}
|
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
// Constant: no changed input
|
|
|
|
MemoInputs::Constant => true,
|
|
|
|
|
|
|
|
// Check whether any of our inputs changed since the
|
|
|
|
// **last point where we were verified** (not since we
|
|
|
|
// last changed). This is important: if we have
|
|
|
|
// memoized values, then an input may have changed in
|
|
|
|
// revision R2, but we found that *our* value was the
|
|
|
|
// same regardless, so our change date is still
|
|
|
|
// R1. But our *verification* date will be R2, and we
|
|
|
|
// are only interested in finding out whether the
|
|
|
|
// input changed *again*.
|
|
|
|
MemoInputs::Tracked { inputs } => {
|
|
|
|
let changed_input = inputs
|
2018-10-09 15:04:25 +00:00
|
|
|
.iter()
|
2019-03-22 08:58:47 +00:00
|
|
|
.filter(|input| input.maybe_changed_since(db, verified_at))
|
2018-10-22 13:52:49 +00:00
|
|
|
.next();
|
|
|
|
|
|
|
|
if let Some(input) = changed_input {
|
|
|
|
debug!(
|
|
|
|
"{:?}::validate_memoized_value: `{:?}` may have changed",
|
|
|
|
Q::default(),
|
|
|
|
input
|
|
|
|
);
|
|
|
|
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
2018-10-15 09:29:21 +00:00
|
|
|
}
|
2018-10-22 13:52:49 +00:00
|
|
|
};
|
2018-10-09 15:04:25 +00:00
|
|
|
|
2018-10-22 13:52:49 +00:00
|
|
|
self.verified_at = revision_now;
|
|
|
|
Some(StampedValue {
|
|
|
|
changed_at: ChangedAt {
|
|
|
|
is_constant,
|
|
|
|
revision: self.changed_at,
|
|
|
|
},
|
|
|
|
value: value.clone(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the memoized value *if* it is known to be update in the given revision.
|
|
|
|
fn probe_memoized_value(&self, revision_now: Revision) -> Option<StampedValue<Q::Value>> {
|
|
|
|
let value = self.value.as_ref()?;
|
|
|
|
|
|
|
|
debug!(
|
|
|
|
"probe_memoized_value(verified_at={:?}, changed_at={:?})",
|
|
|
|
self.verified_at, self.changed_at,
|
|
|
|
);
|
|
|
|
|
|
|
|
if self.verified_at == revision_now {
|
|
|
|
let is_constant = match self.inputs {
|
|
|
|
MemoInputs::Constant => true,
|
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
|
|
|
|
return Some(StampedValue {
|
|
|
|
changed_at: ChangedAt {
|
|
|
|
is_constant,
|
|
|
|
revision: self.changed_at,
|
|
|
|
},
|
|
|
|
value: value.clone(),
|
|
|
|
});
|
2018-10-09 12:53:13 +00:00
|
|
|
}
|
2018-10-22 13:52:49 +00:00
|
|
|
|
|
|
|
None
|
2018-10-09 12:53:13 +00:00
|
|
|
}
|
|
|
|
}
|