2019-02-03 15:47:18 +00:00
|
|
|
use crate::debug::TableEntry;
|
2019-06-25 21:32:24 +00:00
|
|
|
use crate::durability::Durability;
|
2019-04-03 14:00:59 +00:00
|
|
|
use crate::intern_id::InternId;
|
2019-02-04 21:21:24 +00:00
|
|
|
use crate::plumbing::HasQueryGroup;
|
2019-02-03 15:47:18 +00:00
|
|
|
use crate::plumbing::QueryStorageMassOps;
|
|
|
|
use crate::plumbing::QueryStorageOps;
|
2019-06-24 21:38:54 +00:00
|
|
|
use crate::revision::Revision;
|
2019-02-03 15:47:18 +00:00
|
|
|
use crate::Query;
|
2020-07-21 21:59:28 +00:00
|
|
|
use crate::{CycleError, Database, DatabaseKeyIndex, DiscardIf, QueryDb, Runtime, SweepStrategy};
|
2020-06-30 19:27:09 +00:00
|
|
|
use crossbeam_utils::atomic::AtomicCell;
|
2019-02-03 15:47:18 +00:00
|
|
|
use parking_lot::RwLock;
|
|
|
|
use rustc_hash::FxHashMap;
|
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
use std::convert::From;
|
2019-06-16 14:57:22 +00:00
|
|
|
use std::fmt::Debug;
|
2019-02-03 15:47:18 +00:00
|
|
|
use std::hash::Hash;
|
2019-06-16 14:57:22 +00:00
|
|
|
use std::sync::Arc;
|
2019-02-03 15:47:18 +00:00
|
|
|
|
2019-06-26 01:50:00 +00:00
|
|
|
const INTERN_DURABILITY: Durability = Durability::HIGH;
|
|
|
|
|
2019-02-03 15:47:18 +00:00
|
|
|
/// Handles storage where the value is 'derived' by executing a
|
|
|
|
/// function (in contrast to "inputs").
|
2020-07-03 10:46:00 +00:00
|
|
|
pub struct InternedStorage<Q>
|
2019-02-03 15:47:18 +00:00
|
|
|
where
|
2020-07-03 10:46:00 +00:00
|
|
|
Q: Query,
|
2019-02-04 20:01:23 +00:00
|
|
|
Q::Value: InternKey,
|
2019-02-03 15:47:18 +00:00
|
|
|
{
|
2020-06-30 15:18:32 +00:00
|
|
|
group_index: u16,
|
2019-02-03 15:47:18 +00:00
|
|
|
tables: RwLock<InternTables<Q::Key>>,
|
|
|
|
}
|
|
|
|
|
2019-02-04 21:21:24 +00:00
|
|
|
/// Storage for the looking up interned things.
|
2020-07-03 10:46:00 +00:00
|
|
|
pub struct LookupInternedStorage<Q, IQ>
|
2019-02-04 21:21:24 +00:00
|
|
|
where
|
2020-07-03 10:46:00 +00:00
|
|
|
Q: Query,
|
2019-02-04 21:21:24 +00:00
|
|
|
Q::Key: InternKey,
|
|
|
|
Q::Value: Eq + Hash,
|
|
|
|
{
|
2019-03-27 11:33:59 +00:00
|
|
|
phantom: std::marker::PhantomData<(Q::Key, IQ)>,
|
2019-02-04 21:21:24 +00:00
|
|
|
}
|
|
|
|
|
2019-02-03 15:47:18 +00:00
|
|
|
struct InternTables<K> {
|
|
|
|
/// Map from the key to the corresponding intern-index.
|
2019-04-03 14:00:59 +00:00
|
|
|
map: FxHashMap<K, InternId>,
|
2019-02-03 15:47:18 +00:00
|
|
|
|
|
|
|
/// For each valid intern-index, stores the interned value. When
|
|
|
|
/// an interned value is GC'd, the entry is set to
|
|
|
|
/// `InternValue::Free` with the next free item.
|
|
|
|
values: Vec<InternValue<K>>,
|
|
|
|
|
|
|
|
/// Index of the first free intern-index, if any.
|
2019-04-03 14:00:59 +00:00
|
|
|
first_free: Option<InternId>,
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
|
2019-02-04 20:01:23 +00:00
|
|
|
/// Trait implemented for the "key" that results from a
|
|
|
|
/// `#[salsa::intern]` query. This is basically meant to be a
|
|
|
|
/// "newtype"'d `u32`.
|
|
|
|
pub trait InternKey {
|
|
|
|
/// Create an instance of the intern-key from a `u32` value.
|
2019-04-03 14:00:59 +00:00
|
|
|
fn from_intern_id(v: InternId) -> Self;
|
2019-02-04 20:01:23 +00:00
|
|
|
|
|
|
|
/// Extract the `u32` with which the intern-key was created.
|
2019-04-03 14:00:59 +00:00
|
|
|
fn as_intern_id(&self) -> InternId;
|
2019-02-04 20:01:23 +00:00
|
|
|
}
|
|
|
|
|
2019-04-03 14:00:59 +00:00
|
|
|
impl InternKey for InternId {
|
|
|
|
fn from_intern_id(v: InternId) -> InternId {
|
2019-02-04 20:01:23 +00:00
|
|
|
v
|
|
|
|
}
|
|
|
|
|
2019-04-03 14:00:59 +00:00
|
|
|
fn as_intern_id(&self) -> InternId {
|
2019-02-04 20:01:23 +00:00
|
|
|
*self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 15:47:18 +00:00
|
|
|
enum InternValue<K> {
|
|
|
|
/// The value has not been gc'd.
|
2019-06-16 14:57:22 +00:00
|
|
|
Present { slot: Arc<Slot<K>> },
|
2019-02-03 15:47:18 +00:00
|
|
|
|
|
|
|
/// Free-list -- the index is the next
|
2019-04-03 14:00:59 +00:00
|
|
|
Free { next: Option<InternId> },
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
|
2019-06-16 14:57:22 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct Slot<K> {
|
|
|
|
/// Index of this slot in the list of interned values;
|
|
|
|
/// set to None if gc'd.
|
|
|
|
index: InternId,
|
|
|
|
|
2020-06-30 15:18:32 +00:00
|
|
|
/// DatabaseKeyIndex for this slot.
|
|
|
|
database_key_index: DatabaseKeyIndex,
|
|
|
|
|
2019-06-16 14:57:22 +00:00
|
|
|
/// Value that was interned.
|
|
|
|
value: K,
|
|
|
|
|
|
|
|
/// When was this intern'd?
|
|
|
|
///
|
|
|
|
/// (This informs the "changed-at" result)
|
|
|
|
interned_at: Revision,
|
|
|
|
|
|
|
|
/// When was it accessed? Equal to `None` if this slot has
|
|
|
|
/// been garbage collected.
|
|
|
|
///
|
|
|
|
/// This has a subtle interaction with the garbage
|
|
|
|
/// collector. First, we will never GC anything accessed in the
|
|
|
|
/// current revision.
|
|
|
|
///
|
|
|
|
/// To protect a slot from being GC'd, we can therefore update the
|
|
|
|
/// `accessed_at` field to `Some(revision_now)` before releasing
|
|
|
|
/// the read-lock on our interning tables.
|
|
|
|
accessed_at: AtomicCell<Option<Revision>>,
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
impl<Q> std::panic::RefUnwindSafe for InternedStorage<Q>
|
2019-02-03 15:47:18 +00:00
|
|
|
where
|
2020-07-03 10:46:00 +00:00
|
|
|
Q: Query,
|
2019-02-03 15:47:18 +00:00
|
|
|
Q::Key: std::panic::RefUnwindSafe,
|
2019-02-04 20:01:23 +00:00
|
|
|
Q::Value: InternKey,
|
2019-02-03 15:47:18 +00:00
|
|
|
Q::Value: std::panic::RefUnwindSafe,
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-06-16 14:57:22 +00:00
|
|
|
impl<K: Debug + Hash + Eq> InternTables<K> {
|
|
|
|
/// Returns the slot for the given key.
|
|
|
|
///
|
|
|
|
/// The slot will have its "accessed at" field updated to its current revision,
|
|
|
|
/// ensuring that it cannot be GC'd until the current queries complete.
|
|
|
|
fn slot_for_key(&self, key: &K, revision_now: Revision) -> Option<Arc<Slot<K>>> {
|
|
|
|
let index = self.map.get(key)?;
|
|
|
|
Some(self.slot_for_index(*index, revision_now))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the slot at the given index.
|
|
|
|
///
|
|
|
|
/// The slot will have its "accessed at" field updated to its current revision,
|
|
|
|
/// ensuring that it cannot be GC'd until the current queries complete.
|
|
|
|
fn slot_for_index(&self, index: InternId, revision_now: Revision) -> Arc<Slot<K>> {
|
|
|
|
match &self.values[index.as_usize()] {
|
|
|
|
InternValue::Present { slot } => {
|
|
|
|
// Subtle: we must update the "accessed at" to the
|
|
|
|
// current revision *while the lock is held* to
|
|
|
|
// prevent this slot from being GC'd.
|
|
|
|
let updated = slot.try_update_accessed_at(revision_now);
|
|
|
|
assert!(
|
|
|
|
updated,
|
|
|
|
"failed to update slot {:?} while holding read lock",
|
|
|
|
slot
|
|
|
|
);
|
|
|
|
slot.clone()
|
|
|
|
}
|
|
|
|
InternValue::Free { .. } => {
|
|
|
|
panic!("index {:?} is free but should not be", index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-03 15:47:18 +00:00
|
|
|
impl<K> Default for InternTables<K>
|
|
|
|
where
|
|
|
|
K: Eq + Hash,
|
|
|
|
{
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
map: Default::default(),
|
|
|
|
values: Default::default(),
|
|
|
|
first_free: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
impl<Q> InternedStorage<Q>
|
2019-02-03 15:47:18 +00:00
|
|
|
where
|
2020-07-03 10:46:00 +00:00
|
|
|
Q: Query,
|
2019-02-03 15:47:18 +00:00
|
|
|
Q::Key: Eq + Hash + Clone,
|
2019-02-04 20:01:23 +00:00
|
|
|
Q::Value: InternKey,
|
2019-02-03 15:47:18 +00:00
|
|
|
{
|
2019-06-16 14:57:22 +00:00
|
|
|
/// If `key` has already been interned, returns its slot. Otherwise, creates a new slot.
|
|
|
|
///
|
|
|
|
/// In either case, the `accessed_at` field of the slot is updated
|
|
|
|
/// to the current revision, ensuring that the slot cannot be GC'd
|
|
|
|
/// while the current queries execute.
|
2020-07-21 21:59:28 +00:00
|
|
|
fn intern_index(&self, db: &<Q as QueryDb<'_>>::DynDb, key: &Q::Key) -> Arc<Slot<Q::Key>> {
|
2019-02-03 15:47:18 +00:00
|
|
|
if let Some(i) = self.intern_check(db, key) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
let owned_key1 = key.to_owned();
|
|
|
|
let owned_key2 = owned_key1.clone();
|
|
|
|
let revision_now = db.salsa_runtime().current_revision();
|
|
|
|
|
|
|
|
let mut tables = self.tables.write();
|
|
|
|
let tables = &mut *tables;
|
|
|
|
let entry = match tables.map.entry(owned_key1) {
|
|
|
|
Entry::Vacant(entry) => entry,
|
|
|
|
Entry::Occupied(entry) => {
|
|
|
|
// Somebody inserted this key while we were waiting
|
2019-06-16 14:57:22 +00:00
|
|
|
// for the write lock. In this case, we don't need to
|
|
|
|
// update the `accessed_at` field because they should
|
|
|
|
// have already done so!
|
2019-02-03 15:47:18 +00:00
|
|
|
let index = *entry.get();
|
2019-03-30 09:43:16 +00:00
|
|
|
match &tables.values[index.as_usize()] {
|
2019-06-16 14:57:22 +00:00
|
|
|
InternValue::Present { slot } => {
|
|
|
|
debug_assert_eq!(owned_key2, slot.value);
|
|
|
|
debug_assert_eq!(slot.accessed_at.load(), Some(revision_now));
|
|
|
|
return slot.clone();
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
InternValue::Free { .. } => {
|
|
|
|
panic!("key {:?} should be present but is not", key,);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-06-16 14:57:22 +00:00
|
|
|
let create_slot = |index: InternId| {
|
2020-06-30 15:18:32 +00:00
|
|
|
let database_key_index = DatabaseKeyIndex {
|
|
|
|
group_index: self.group_index,
|
|
|
|
query_index: Q::QUERY_INDEX,
|
|
|
|
key_index: index.as_u32(),
|
|
|
|
};
|
2019-06-16 14:57:22 +00:00
|
|
|
Arc::new(Slot {
|
|
|
|
index,
|
2020-06-30 15:18:32 +00:00
|
|
|
database_key_index,
|
2019-06-16 14:57:22 +00:00
|
|
|
value: owned_key2,
|
|
|
|
interned_at: revision_now,
|
|
|
|
accessed_at: AtomicCell::new(Some(revision_now)),
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
let (slot, index);
|
|
|
|
match tables.first_free {
|
2019-02-03 15:47:18 +00:00
|
|
|
None => {
|
2019-06-16 14:57:22 +00:00
|
|
|
index = InternId::from(tables.values.len());
|
|
|
|
slot = create_slot(index);
|
|
|
|
tables
|
|
|
|
.values
|
|
|
|
.push(InternValue::Present { slot: slot.clone() });
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Some(i) => {
|
2019-06-16 14:57:22 +00:00
|
|
|
index = i;
|
|
|
|
slot = create_slot(index);
|
|
|
|
|
2019-03-30 09:43:16 +00:00
|
|
|
let next_free = match &tables.values[i.as_usize()] {
|
2019-02-03 15:47:18 +00:00
|
|
|
InternValue::Free { next } => *next,
|
2019-06-16 14:57:22 +00:00
|
|
|
InternValue::Present { slot } => {
|
2019-02-03 15:47:18 +00:00
|
|
|
panic!(
|
|
|
|
"index {:?} was supposed to be free but contains {:?}",
|
2019-06-16 14:57:22 +00:00
|
|
|
i, slot.value
|
2019-02-03 15:47:18 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-06-16 14:57:22 +00:00
|
|
|
tables.values[index.as_usize()] = InternValue::Present { slot: slot.clone() };
|
2019-02-03 15:47:18 +00:00
|
|
|
tables.first_free = next_free;
|
|
|
|
}
|
2019-06-16 14:57:22 +00:00
|
|
|
}
|
2019-02-03 15:47:18 +00:00
|
|
|
|
|
|
|
entry.insert(index);
|
|
|
|
|
2019-06-16 14:57:22 +00:00
|
|
|
slot
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 21:59:28 +00:00
|
|
|
fn intern_check(
|
|
|
|
&self,
|
|
|
|
db: &<Q as QueryDb<'_>>::DynDb,
|
|
|
|
key: &Q::Key,
|
|
|
|
) -> Option<Arc<Slot<Q::Key>>> {
|
2019-02-03 15:47:18 +00:00
|
|
|
let revision_now = db.salsa_runtime().current_revision();
|
2019-06-16 14:57:22 +00:00
|
|
|
let slot = self.tables.read().slot_for_key(key, revision_now)?;
|
|
|
|
Some(slot)
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Given an index, lookup and clone its value, updating the
|
|
|
|
/// `accessed_at` time if necessary.
|
2020-07-21 21:59:28 +00:00
|
|
|
fn lookup_value(&self, db: &<Q as QueryDb<'_>>::DynDb, index: InternId) -> Arc<Slot<Q::Key>> {
|
2019-02-03 15:47:18 +00:00
|
|
|
let revision_now = db.salsa_runtime().current_revision();
|
2019-06-16 14:57:22 +00:00
|
|
|
self.tables.read().slot_for_index(index, revision_now)
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
impl<Q> QueryStorageOps<Q> for InternedStorage<Q>
|
2019-02-03 15:47:18 +00:00
|
|
|
where
|
2020-07-03 10:46:00 +00:00
|
|
|
Q: Query,
|
2019-02-04 20:01:23 +00:00
|
|
|
Q::Value: InternKey,
|
2019-02-03 15:47:18 +00:00
|
|
|
{
|
2020-06-30 15:18:32 +00:00
|
|
|
fn new(group_index: u16) -> Self {
|
2020-06-30 10:26:55 +00:00
|
|
|
InternedStorage {
|
2020-06-30 15:18:32 +00:00
|
|
|
group_index,
|
2020-06-30 10:26:55 +00:00
|
|
|
tables: RwLock::new(InternTables::default()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 09:49:19 +00:00
|
|
|
fn fmt_index(
|
|
|
|
&self,
|
2020-07-21 21:59:28 +00:00
|
|
|
db: &<Q as QueryDb<'_>>::DynDb,
|
2020-07-01 09:49:19 +00:00
|
|
|
index: DatabaseKeyIndex,
|
|
|
|
fmt: &mut std::fmt::Formatter<'_>,
|
|
|
|
) -> std::fmt::Result {
|
|
|
|
assert_eq!(index.group_index, self.group_index);
|
|
|
|
assert_eq!(index.query_index, Q::QUERY_INDEX);
|
|
|
|
let intern_id = InternId::from(index.key_index);
|
|
|
|
let slot = self.lookup_value(db, intern_id);
|
|
|
|
write!(fmt, "{}({:?})", Q::QUERY_NAME, slot.value)
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
fn maybe_changed_since(
|
|
|
|
&self,
|
2020-07-21 21:59:28 +00:00
|
|
|
db: &<Q as QueryDb<'_>>::DynDb,
|
2020-07-03 10:46:00 +00:00
|
|
|
input: DatabaseKeyIndex,
|
|
|
|
revision: Revision,
|
|
|
|
) -> bool {
|
2020-06-30 15:56:14 +00:00
|
|
|
assert_eq!(input.group_index, self.group_index);
|
|
|
|
assert_eq!(input.query_index, Q::QUERY_INDEX);
|
|
|
|
let intern_id = InternId::from(input.key_index);
|
|
|
|
let slot = self.lookup_value(db, intern_id);
|
|
|
|
slot.maybe_changed_since(db, revision)
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
fn try_fetch(
|
|
|
|
&self,
|
2020-07-21 21:59:28 +00:00
|
|
|
db: &<Q as QueryDb<'_>>::DynDb,
|
2020-07-03 10:46:00 +00:00
|
|
|
key: &Q::Key,
|
|
|
|
) -> Result<Q::Value, CycleError<DatabaseKeyIndex>> {
|
2019-06-16 14:57:22 +00:00
|
|
|
let slot = self.intern_index(db, key);
|
|
|
|
let changed_at = slot.interned_at;
|
|
|
|
let index = slot.index;
|
2020-06-30 15:56:14 +00:00
|
|
|
db.salsa_runtime().report_query_read(
|
|
|
|
slot.database_key_index,
|
|
|
|
INTERN_DURABILITY,
|
|
|
|
changed_at,
|
|
|
|
);
|
2019-06-16 14:57:22 +00:00
|
|
|
Ok(<Q::Value>::from_intern_id(index))
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 21:59:28 +00:00
|
|
|
fn durability(&self, _db: &<Q as QueryDb<'_>>::DynDb, _key: &Q::Key) -> Durability {
|
2019-06-26 01:50:00 +00:00
|
|
|
INTERN_DURABILITY
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
|
|
|
|
2020-07-21 21:59:28 +00:00
|
|
|
fn entries<C>(&self, _db: &<Q as QueryDb<'_>>::DynDb) -> C
|
2019-02-03 15:47:18 +00:00
|
|
|
where
|
|
|
|
C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>,
|
|
|
|
{
|
|
|
|
let tables = self.tables.read();
|
|
|
|
tables
|
|
|
|
.map
|
|
|
|
.iter()
|
2019-04-03 14:00:59 +00:00
|
|
|
.map(|(key, index)| {
|
|
|
|
TableEntry::new(key.clone(), Some(<Q::Value>::from_intern_id(*index)))
|
|
|
|
})
|
2019-02-03 15:47:18 +00:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
impl<Q> QueryStorageMassOps for InternedStorage<Q>
|
2019-02-03 15:47:18 +00:00
|
|
|
where
|
2020-07-03 10:46:00 +00:00
|
|
|
Q: Query,
|
2019-02-04 20:01:23 +00:00
|
|
|
Q::Value: InternKey,
|
2019-02-03 15:47:18 +00:00
|
|
|
{
|
2020-07-03 09:55:19 +00:00
|
|
|
fn sweep(&self, runtime: &Runtime, strategy: SweepStrategy) {
|
2019-02-03 15:47:18 +00:00
|
|
|
let mut tables = self.tables.write();
|
2020-07-03 09:55:19 +00:00
|
|
|
let last_changed = runtime.last_changed_revision(INTERN_DURABILITY);
|
|
|
|
let revision_now = runtime.current_revision();
|
2019-02-03 15:47:18 +00:00
|
|
|
let InternTables {
|
|
|
|
map,
|
|
|
|
values,
|
|
|
|
first_free,
|
|
|
|
} = &mut *tables;
|
|
|
|
map.retain(|key, intern_index| {
|
2019-06-16 14:57:22 +00:00
|
|
|
match strategy.discard_if {
|
|
|
|
DiscardIf::Never => true,
|
2019-03-22 09:13:07 +00:00
|
|
|
|
|
|
|
// NB: Interned keys *never* discard keys unless they
|
|
|
|
// are outdated, regardless of the sweep strategy. This is
|
|
|
|
// because interned queries are not deterministic;
|
|
|
|
// if we were to remove a value from the current revision,
|
|
|
|
// and the query were later executed again, it would not necessarily
|
|
|
|
// produce the same intern key the second time. This would wreak
|
|
|
|
// havoc. See the test `discard_during_same_revision` for an example.
|
|
|
|
//
|
|
|
|
// Keys that have not (yet) been accessed during this
|
|
|
|
// revision don't have this problem. Anything
|
|
|
|
// dependent on them would regard itself as dirty if
|
|
|
|
// they are removed and also be forced to re-execute.
|
2019-06-16 14:57:22 +00:00
|
|
|
DiscardIf::Always | DiscardIf::Outdated => match &values[intern_index.as_usize()] {
|
|
|
|
InternValue::Present { slot, .. } => {
|
2019-06-26 01:50:00 +00:00
|
|
|
if slot.try_collect(last_changed, revision_now) {
|
2019-06-16 14:57:22 +00:00
|
|
|
values[intern_index.as_usize()] =
|
|
|
|
InternValue::Free { next: *first_free };
|
|
|
|
*first_free = Some(*intern_index);
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
2019-02-03 15:47:18 +00:00
|
|
|
|
|
|
|
InternValue::Free { .. } => {
|
|
|
|
panic!(
|
|
|
|
"key {:?} maps to index {:?} which is free",
|
|
|
|
key, intern_index
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2020-07-22 16:22:16 +00:00
|
|
|
fn purge(&self) {
|
|
|
|
*self.tables.write() = Default::default();
|
|
|
|
}
|
2019-02-03 15:47:18 +00:00
|
|
|
}
|
2019-02-04 21:21:24 +00:00
|
|
|
|
2020-07-22 09:36:20 +00:00
|
|
|
// Workaround for
|
|
|
|
// ```
|
|
|
|
// IQ: for<'d> QueryDb<
|
|
|
|
// 'd,
|
|
|
|
// DynDb = <Q as QueryDb<'d>>::DynDb,
|
|
|
|
// Group = <Q as QueryDb<'d>>::Group,
|
|
|
|
// GroupStorage = <Q as QueryDb<'d>>::GroupStorage,
|
|
|
|
// >,
|
|
|
|
// ```
|
|
|
|
// not working to make rustc know DynDb, Group and GroupStorage being the same in `Q` and `IQ`
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub trait EqualDynDb<'d, IQ>: QueryDb<'d>
|
2019-02-04 21:21:24 +00:00
|
|
|
where
|
2020-07-22 09:36:20 +00:00
|
|
|
IQ: QueryDb<'d>,
|
|
|
|
{
|
|
|
|
fn convert_db(d: &Self::DynDb) -> &IQ::DynDb;
|
|
|
|
fn convert_group_storage(d: &Self::GroupStorage) -> &IQ::GroupStorage;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'d, IQ, Q> EqualDynDb<'d, IQ> for Q
|
|
|
|
where
|
|
|
|
Q: QueryDb<'d, DynDb = IQ::DynDb, Group = IQ::Group, GroupStorage = IQ::GroupStorage>,
|
|
|
|
Q::DynDb: HasQueryGroup<Q::Group>,
|
|
|
|
IQ: QueryDb<'d>,
|
|
|
|
{
|
|
|
|
fn convert_db(d: &Self::DynDb) -> &IQ::DynDb {
|
|
|
|
d
|
|
|
|
}
|
|
|
|
fn convert_group_storage(d: &Self::GroupStorage) -> &IQ::GroupStorage {
|
|
|
|
d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
impl<Q, IQ> QueryStorageOps<Q> for LookupInternedStorage<Q, IQ>
|
2019-02-04 21:21:24 +00:00
|
|
|
where
|
2020-07-03 10:46:00 +00:00
|
|
|
Q: Query,
|
2019-02-04 21:21:24 +00:00
|
|
|
Q::Key: InternKey,
|
|
|
|
Q::Value: Eq + Hash,
|
2020-07-22 09:36:20 +00:00
|
|
|
IQ: Query<Key = Q::Value, Value = Q::Key, Storage = InternedStorage<IQ>>,
|
|
|
|
for<'d> Q: EqualDynDb<'d, IQ>,
|
2019-02-04 21:21:24 +00:00
|
|
|
{
|
2020-06-30 10:26:55 +00:00
|
|
|
fn new(_group_index: u16) -> Self {
|
|
|
|
LookupInternedStorage {
|
|
|
|
phantom: std::marker::PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 09:49:19 +00:00
|
|
|
fn fmt_index(
|
|
|
|
&self,
|
2020-07-22 09:36:20 +00:00
|
|
|
db: &<Q as QueryDb<'_>>::DynDb,
|
2020-07-01 09:49:19 +00:00
|
|
|
index: DatabaseKeyIndex,
|
|
|
|
fmt: &mut std::fmt::Formatter<'_>,
|
|
|
|
) -> std::fmt::Result {
|
2020-07-21 21:59:28 +00:00
|
|
|
let group_storage =
|
|
|
|
<<Q as QueryDb<'_>>::DynDb as HasQueryGroup<Q::Group>>::group_storage(db);
|
2020-07-22 09:36:20 +00:00
|
|
|
let interned_storage = IQ::query_storage(Q::convert_group_storage(group_storage));
|
|
|
|
interned_storage.fmt_index(Q::convert_db(db), index, fmt)
|
2020-07-01 09:49:19 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
fn maybe_changed_since(
|
|
|
|
&self,
|
2020-07-22 09:36:20 +00:00
|
|
|
db: &<Q as QueryDb<'_>>::DynDb,
|
2020-07-03 10:46:00 +00:00
|
|
|
input: DatabaseKeyIndex,
|
|
|
|
revision: Revision,
|
|
|
|
) -> bool {
|
2020-07-21 21:59:28 +00:00
|
|
|
let group_storage =
|
|
|
|
<<Q as QueryDb<'_>>::DynDb as HasQueryGroup<Q::Group>>::group_storage(db);
|
2020-07-22 09:36:20 +00:00
|
|
|
let interned_storage = IQ::query_storage(Q::convert_group_storage(group_storage));
|
|
|
|
interned_storage.maybe_changed_since(Q::convert_db(db), input, revision)
|
2020-06-30 15:56:14 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
fn try_fetch(
|
|
|
|
&self,
|
2020-07-22 09:36:20 +00:00
|
|
|
db: &<Q as QueryDb<'_>>::DynDb,
|
2020-07-03 10:46:00 +00:00
|
|
|
key: &Q::Key,
|
|
|
|
) -> Result<Q::Value, CycleError<DatabaseKeyIndex>> {
|
2019-04-03 14:00:59 +00:00
|
|
|
let index = key.as_intern_id();
|
2020-07-21 21:59:28 +00:00
|
|
|
let group_storage =
|
|
|
|
<<Q as QueryDb<'_>>::DynDb as HasQueryGroup<Q::Group>>::group_storage(db);
|
2020-07-22 09:36:20 +00:00
|
|
|
let interned_storage = IQ::query_storage(Q::convert_group_storage(group_storage));
|
|
|
|
let slot = interned_storage.lookup_value(Q::convert_db(db), index);
|
2019-06-16 14:57:22 +00:00
|
|
|
let value = slot.value.clone();
|
2019-06-22 03:12:51 +00:00
|
|
|
let interned_at = slot.interned_at;
|
2020-06-30 15:56:14 +00:00
|
|
|
db.salsa_runtime().report_query_read(
|
|
|
|
slot.database_key_index,
|
|
|
|
INTERN_DURABILITY,
|
|
|
|
interned_at,
|
|
|
|
);
|
2019-02-04 21:21:24 +00:00
|
|
|
Ok(value)
|
|
|
|
}
|
|
|
|
|
2020-07-22 09:36:20 +00:00
|
|
|
fn durability(&self, _db: &<Q as QueryDb<'_>>::DynDb, _key: &Q::Key) -> Durability {
|
2019-06-26 01:50:00 +00:00
|
|
|
INTERN_DURABILITY
|
2019-02-04 21:21:24 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 09:36:20 +00:00
|
|
|
fn entries<C>(&self, db: &<Q as QueryDb<'_>>::DynDb) -> C
|
2019-02-04 21:21:24 +00:00
|
|
|
where
|
|
|
|
C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>,
|
|
|
|
{
|
2020-07-21 21:59:28 +00:00
|
|
|
let group_storage =
|
|
|
|
<<Q as QueryDb<'_>>::DynDb as HasQueryGroup<Q::Group>>::group_storage(db);
|
2020-07-22 09:36:20 +00:00
|
|
|
let interned_storage = IQ::query_storage(Q::convert_group_storage(group_storage));
|
2019-02-04 21:21:24 +00:00
|
|
|
let tables = interned_storage.tables.read();
|
|
|
|
tables
|
|
|
|
.map
|
|
|
|
.iter()
|
2019-04-03 14:00:59 +00:00
|
|
|
.map(|(key, index)| {
|
|
|
|
TableEntry::new(<Q::Key>::from_intern_id(*index), Some(key.clone()))
|
|
|
|
})
|
2019-02-04 21:21:24 +00:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
impl<Q, IQ> QueryStorageMassOps for LookupInternedStorage<Q, IQ>
|
2019-02-04 21:21:24 +00:00
|
|
|
where
|
2020-07-03 10:46:00 +00:00
|
|
|
Q: Query,
|
2019-02-04 21:21:24 +00:00
|
|
|
Q::Key: InternKey,
|
|
|
|
Q::Value: Eq + Hash,
|
2020-07-22 09:36:20 +00:00
|
|
|
IQ: Query<Key = Q::Value, Value = Q::Key>,
|
2019-02-04 21:21:24 +00:00
|
|
|
{
|
2020-07-03 09:55:19 +00:00
|
|
|
fn sweep(&self, _: &Runtime, _strategy: SweepStrategy) {}
|
2020-07-22 16:22:16 +00:00
|
|
|
fn purge(&self) {}
|
2019-02-04 21:21:24 +00:00
|
|
|
}
|
2019-06-16 14:57:22 +00:00
|
|
|
|
|
|
|
impl<K> Slot<K> {
|
2020-07-03 10:46:00 +00:00
|
|
|
fn maybe_changed_since<DB: ?Sized + Database>(&self, db: &DB, revision: Revision) -> bool {
|
2020-06-30 15:56:14 +00:00
|
|
|
let revision_now = db.salsa_runtime().current_revision();
|
|
|
|
if !self.try_update_accessed_at(revision_now) {
|
|
|
|
// if we failed to update accessed-at, then this slot was garbage collected
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
// otherwise, compare the interning with revision
|
|
|
|
self.interned_at > revision
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-16 14:57:22 +00:00
|
|
|
/// Updates the `accessed_at` time to be `revision_now` (if
|
|
|
|
/// necessary). Returns true if the update was successful, or
|
|
|
|
/// false if the slot has been GC'd in the interim.
|
|
|
|
fn try_update_accessed_at(&self, revision_now: Revision) -> bool {
|
|
|
|
if let Some(accessed_at) = self.accessed_at.load() {
|
|
|
|
match self
|
|
|
|
.accessed_at
|
|
|
|
.compare_exchange(Some(accessed_at), Some(revision_now))
|
|
|
|
{
|
|
|
|
Ok(_) => true,
|
|
|
|
Err(Some(r)) => {
|
|
|
|
// Somebody was racing with us to update the field -- but they
|
|
|
|
// also updated it to revision now, so that's cool.
|
|
|
|
debug_assert_eq!(r, revision_now);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
Err(None) => {
|
|
|
|
// The garbage collector was racing with us and it swept this
|
|
|
|
// slot before we could mark it as accessed.
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Invoked during sweeping to try and collect this slot. Fails if
|
2019-06-26 01:50:00 +00:00
|
|
|
/// the slot has been accessed since the intern durability last
|
|
|
|
/// changed, because in that case there may be outstanding
|
|
|
|
/// references that are still considered valid. Note that this
|
|
|
|
/// access could be racing with the attempt to collect (in
|
2019-06-16 14:57:22 +00:00
|
|
|
/// particular, when verifying dependencies).
|
2019-06-26 01:50:00 +00:00
|
|
|
fn try_collect(&self, last_changed: Revision, revision_now: Revision) -> bool {
|
2019-06-16 14:57:22 +00:00
|
|
|
let accessed_at = self.accessed_at.load().unwrap();
|
2019-06-26 01:50:00 +00:00
|
|
|
if accessed_at < last_changed {
|
2019-06-16 14:57:22 +00:00
|
|
|
match self.accessed_at.compare_exchange(Some(accessed_at), None) {
|
|
|
|
Ok(_) => true,
|
|
|
|
Err(r) => {
|
|
|
|
// The only one racing with us can be a
|
|
|
|
// verification attempt, which will always bump
|
|
|
|
// `accessed_at` to the current revision.
|
|
|
|
debug_assert_eq!(r, Some(revision_now));
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-16 15:15:05 +00:00
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
/// Check that `Slot<Q, MP>: Send + Sync` as long as
|
2019-06-21 03:46:51 +00:00
|
|
|
/// `DB::DatabaseData: Send + Sync`, which in turn implies that
|
|
|
|
/// `Q::Key: Send + Sync`, `Q::Value: Send + Sync`.
|
|
|
|
#[allow(dead_code)]
|
|
|
|
fn check_send_sync<K>()
|
|
|
|
where
|
|
|
|
K: Send + Sync,
|
|
|
|
{
|
|
|
|
fn is_send_sync<T: Send + Sync>() {}
|
|
|
|
is_send_sync::<Slot<K>>();
|
|
|
|
}
|
|
|
|
|
2020-07-03 10:46:00 +00:00
|
|
|
/// Check that `Slot<Q, MP>: 'static` as long as
|
2019-06-21 03:46:51 +00:00
|
|
|
/// `DB::DatabaseData: 'static`, which in turn implies that
|
|
|
|
/// `Q::Key: 'static`, `Q::Value: 'static`.
|
|
|
|
#[allow(dead_code)]
|
|
|
|
fn check_static<K>()
|
|
|
|
where
|
|
|
|
K: 'static,
|
|
|
|
{
|
|
|
|
fn is_static<T: 'static>() {}
|
|
|
|
is_static::<Slot<K>>();
|
|
|
|
}
|