Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ra-salsa/src/input.rs')
| -rw-r--r-- | crates/ra-salsa/src/input.rs | 371 |
1 files changed, 0 insertions, 371 deletions
diff --git a/crates/ra-salsa/src/input.rs b/crates/ra-salsa/src/input.rs deleted file mode 100644 index 4992a0c727..0000000000 --- a/crates/ra-salsa/src/input.rs +++ /dev/null @@ -1,371 +0,0 @@ -use crate::debug::TableEntry; -use crate::durability::Durability; -use crate::hash::FxIndexMap; -use crate::plumbing::CycleRecoveryStrategy; -use crate::plumbing::InputQueryStorageOps; -use crate::plumbing::QueryStorageMassOps; -use crate::plumbing::QueryStorageOps; -use crate::revision::Revision; -use crate::runtime::StampedValue; -use crate::Database; -use crate::Query; -use crate::Runtime; -use crate::{DatabaseKeyIndex, QueryDb}; -use indexmap::map::Entry; -use parking_lot::RwLock; -use std::iter; -use tracing::trace; - -/// 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<Q> -where - Q: Query, -{ - group_index: u16, - slots: RwLock<FxIndexMap<Q::Key, Slot<Q::Value>>>, -} - -struct Slot<V> { - key_index: u32, - stamped_value: RwLock<StampedValue<V>>, -} - -impl<Q> std::panic::RefUnwindSafe for InputStorage<Q> -where - Q: Query, - Q::Key: std::panic::RefUnwindSafe, - Q::Value: std::panic::RefUnwindSafe, -{ -} - -impl<Q> QueryStorageOps<Q> for InputStorage<Q> -where - Q: Query, -{ - const CYCLE_STRATEGY: crate::plumbing::CycleRecoveryStrategy = CycleRecoveryStrategy::Panic; - - fn new(group_index: u16) -> Self { - InputStorage { group_index, slots: Default::default() } - } - - fn fmt_index( - &self, - _db: &<Q as QueryDb<'_>>::DynDb, - index: u32, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - let slot_map = self.slots.read(); - let key = slot_map.get_index(index as usize).unwrap().0; - write!(fmt, "{}({:?})", Q::QUERY_NAME, key) - } - - fn maybe_changed_after( - &self, - db: &<Q as QueryDb<'_>>::DynDb, - index: u32, - revision: Revision, - ) -> bool { - debug_assert!(revision < db.salsa_runtime().current_revision()); - let slots = &self.slots.read(); - let Some((_, slot)) = slots.get_index(index as usize) else { - return true; - }; - - trace!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); - - let changed_at = slot.stamped_value.read().changed_at; - - trace!("maybe_changed_after: changed_at = {:?}", changed_at); - - changed_at > revision - } - - fn fetch(&self, db: &<Q as QueryDb<'_>>::DynDb, key: &Q::Key) -> Q::Value { - db.unwind_if_cancelled(); - - let slots = &self.slots.read(); - let slot = slots - .get(key) - .unwrap_or_else(|| panic!("no value set for {:?}({:?})", Q::default(), key)); - - let StampedValue { value, durability, changed_at } = slot.stamped_value.read().clone(); - - db.salsa_runtime().report_query_read_and_unwind_if_cycle_resulted( - DatabaseKeyIndex { - group_index: self.group_index, - query_index: Q::QUERY_INDEX, - key_index: slot.key_index, - }, - durability, - changed_at, - ); - - value - } - - fn durability(&self, _db: &<Q as QueryDb<'_>>::DynDb, key: &Q::Key) -> Durability { - match self.slots.read().get(key) { - Some(slot) => slot.stamped_value.read().durability, - None => panic!("no value set for {:?}({:?})", Q::default(), key), - } - } - - fn entries<C>(&self, _db: &<Q as QueryDb<'_>>::DynDb) -> C - where - C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>, - { - let slots = self.slots.read(); - slots - .iter() - .map(|(key, slot)| { - TableEntry::new(key.clone(), Some(slot.stamped_value.read().value.clone())) - }) - .collect() - } -} - -impl<Q> QueryStorageMassOps for InputStorage<Q> -where - Q: Query, -{ - fn purge(&self) { - *self.slots.write() = Default::default(); - } -} - -impl<Q> InputQueryStorageOps<Q> for InputStorage<Q> -where - Q: Query, -{ - fn set(&self, runtime: &mut Runtime, key: &Q::Key, value: Q::Value, durability: Durability) { - tracing::trace!("{:?}({:?}) = {:?} ({:?})", Q::default(), key, value, durability); - - // The value is changing, so we need a new revision (*). We also - // need to update the 'last changed' revision by invoking - // `guard.mark_durability_as_changed`. - // - // CAREFUL: This will block until the global revision lock can - // be acquired. If there are still queries executing, they may - // need to read from this input. Therefore, we wait to acquire - // the lock on `map` until we also hold the global query write - // lock. - // - // (*) Technically, since you can't presently access an input - // for a non-existent key, and you can't enumerate the set of - // keys, we only need a new revision if the key used to - // exist. But we may add such methods in the future and this - // case doesn't generally seem worth optimizing for. - runtime.with_incremented_revision(|next_revision| { - let mut slots = self.slots.write(); - - // Do this *after* we acquire the lock, so that we are not - // racing with somebody else to modify this same cell. - // (Otherwise, someone else might write a *newer* revision - // into the same cell while we block on the lock.) - let stamped_value = StampedValue { value, durability, changed_at: next_revision }; - - match slots.entry(key.clone()) { - Entry::Occupied(entry) => { - let mut slot_stamped_value = entry.get().stamped_value.write(); - let old_durability = slot_stamped_value.durability; - *slot_stamped_value = stamped_value; - Some(old_durability) - } - - Entry::Vacant(entry) => { - let key_index = entry.index() as u32; - entry.insert(Slot { key_index, stamped_value: RwLock::new(stamped_value) }); - None - } - } - }); - } -} - -/// Same as `InputStorage`, but optimized for queries that take no inputs. -pub struct UnitInputStorage<Q> -where - Q: Query<Key = ()>, -{ - slot: UnitSlot<Q::Value>, -} - -struct UnitSlot<V> { - database_key_index: DatabaseKeyIndex, - stamped_value: RwLock<Option<StampedValue<V>>>, -} - -impl<Q> std::panic::RefUnwindSafe for UnitInputStorage<Q> -where - Q: Query<Key = ()>, - Q::Key: std::panic::RefUnwindSafe, - Q::Value: std::panic::RefUnwindSafe, -{ -} - -impl<Q> QueryStorageOps<Q> for UnitInputStorage<Q> -where - Q: Query<Key = ()>, -{ - const CYCLE_STRATEGY: crate::plumbing::CycleRecoveryStrategy = CycleRecoveryStrategy::Panic; - - fn new(group_index: u16) -> Self { - let database_key_index = - DatabaseKeyIndex { group_index, query_index: Q::QUERY_INDEX, key_index: 0 }; - UnitInputStorage { slot: UnitSlot { database_key_index, stamped_value: RwLock::new(None) } } - } - - fn fmt_index( - &self, - _db: &<Q as QueryDb<'_>>::DynDb, - _index: u32, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - write!(fmt, "{}", Q::QUERY_NAME) - } - - fn maybe_changed_after( - &self, - db: &<Q as QueryDb<'_>>::DynDb, - _index: u32, - revision: Revision, - ) -> bool { - debug_assert!(revision < db.salsa_runtime().current_revision()); - - trace!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,); - - let Some(value) = &*self.slot.stamped_value.read() else { - return true; - }; - let changed_at = value.changed_at; - - trace!("maybe_changed_after: changed_at = {:?}", changed_at); - - changed_at > revision - } - - fn fetch(&self, db: &<Q as QueryDb<'_>>::DynDb, &(): &Q::Key) -> Q::Value { - db.unwind_if_cancelled(); - - let StampedValue { value, durability, changed_at } = self - .slot - .stamped_value - .read() - .clone() - .unwrap_or_else(|| panic!("no value set for {:?}", Q::default())); - - db.salsa_runtime().report_query_read_and_unwind_if_cycle_resulted( - self.slot.database_key_index, - durability, - changed_at, - ); - - value - } - - fn durability(&self, _db: &<Q as QueryDb<'_>>::DynDb, &(): &Q::Key) -> Durability { - match &*self.slot.stamped_value.read() { - Some(stamped_value) => stamped_value.durability, - None => panic!("no value set for {:?}", Q::default(),), - } - } - - fn entries<C>(&self, _db: &<Q as QueryDb<'_>>::DynDb) -> C - where - C: std::iter::FromIterator<TableEntry<Q::Key, Q::Value>>, - { - iter::once(TableEntry::new( - (), - self.slot.stamped_value.read().as_ref().map(|it| it.value.clone()), - )) - .collect() - } -} - -impl<Q> QueryStorageMassOps for UnitInputStorage<Q> -where - Q: Query<Key = ()>, -{ - fn purge(&self) { - *self.slot.stamped_value.write() = Default::default(); - } -} - -impl<Q> InputQueryStorageOps<Q> for UnitInputStorage<Q> -where - Q: Query<Key = ()>, -{ - fn set(&self, runtime: &mut Runtime, (): &Q::Key, value: Q::Value, durability: Durability) { - tracing::trace!("{:?} = {:?} ({:?})", Q::default(), value, durability); - - // The value is changing, so we need a new revision (*). We also - // need to update the 'last changed' revision by invoking - // `guard.mark_durability_as_changed`. - // - // CAREFUL: This will block until the global revision lock can - // be acquired. If there are still queries executing, they may - // need to read from this input. Therefore, we wait to acquire - // the lock on `map` until we also hold the global query write - // lock. - // - // (*) Technically, since you can't presently access an input - // for a non-existent key, and you can't enumerate the set of - // keys, we only need a new revision if the key used to - // exist. But we may add such methods in the future and this - // case doesn't generally seem worth optimizing for. - runtime.with_incremented_revision(|next_revision| { - let mut stamped_value_slot = self.slot.stamped_value.write(); - - // Do this *after* we acquire the lock, so that we are not - // racing with somebody else to modify this same cell. - // (Otherwise, someone else might write a *newer* revision - // into the same cell while we block on the lock.) - let stamped_value = StampedValue { value, durability, changed_at: next_revision }; - - match &mut *stamped_value_slot { - Some(slot_stamped_value) => { - let old_durability = slot_stamped_value.durability; - *slot_stamped_value = stamped_value; - Some(old_durability) - } - - stamped_value_slot @ None => { - *stamped_value_slot = Some(stamped_value); - None - } - } - }); - } -} - -/// Check that `Slot<Q, MP>: Send + Sync` as long as -/// `DB::DatabaseData: Send + Sync`, which in turn implies that -/// `Q::Key: Send + Sync`, `Q::Value: Send + Sync`. -#[allow(dead_code)] -fn check_send_sync<Q>() -where - Q: Query, - Q::Key: Send + Sync, - Q::Value: Send + Sync, -{ - fn is_send_sync<T: Send + Sync>() {} - is_send_sync::<Slot<Q::Value>>(); - is_send_sync::<UnitSlot<Q::Value>>(); -} - -/// Check that `Slot<Q, MP>: 'static` as long as -/// `DB::DatabaseData: 'static`, which in turn implies that -/// `Q::Key: 'static`, `Q::Value: 'static`. -#[allow(dead_code)] -fn check_static<Q>() -where - Q: Query + 'static, - Q::Key: 'static, - Q::Value: 'static, -{ - fn is_static<T: 'static>() {} - is_static::<Slot<Q::Value>>(); - is_static::<UnitSlot<Q::Value>>(); -} |