salsa/
interned.rs

1use dashmap::SharedValue;
2
3use crate::accumulator::accumulated_map::InputAccumulatedValues;
4use crate::durability::Durability;
5use crate::ingredient::{fmt_index, MaybeChangedAfter};
6use crate::key::InputDependencyIndex;
7use crate::plumbing::{IngredientIndices, Jar};
8use crate::table::memo::MemoTable;
9use crate::table::sync::SyncTable;
10use crate::table::Slot;
11use crate::zalsa::{IngredientIndex, Zalsa};
12use crate::zalsa_local::QueryOrigin;
13use crate::{Database, DatabaseKeyIndex, Id};
14use std::any::TypeId;
15use std::fmt;
16use std::hash::{BuildHasher, Hash, Hasher};
17use std::marker::PhantomData;
18use std::path::{Path, PathBuf};
19
20use super::hash::FxDashMap;
21use super::ingredient::Ingredient;
22use super::Revision;
23
24pub trait Configuration: Sized + 'static {
25    const DEBUG_NAME: &'static str;
26
27    /// The fields of the struct being interned.
28    type Fields<'db>: InternedData;
29
30    /// The end user struct
31    type Struct<'db>: Copy;
32
33    /// Create an end-user struct from the salsa id
34    ///
35    /// This call is an "end-step" to the tracked struct lookup/creation
36    /// process in a given revision: it occurs only when the struct is newly
37    /// created or, if a struct is being reused, after we have updated its
38    /// fields (or confirmed it is green and no updates are required).
39    fn struct_from_id<'db>(id: Id) -> Self::Struct<'db>;
40
41    /// Deref the struct to yield the underlying id.
42    fn deref_struct(s: Self::Struct<'_>) -> Id;
43}
44
45pub trait InternedData: Sized + Eq + Hash + Clone + Sync + Send {}
46impl<T: Eq + Hash + Clone + Sync + Send> InternedData for T {}
47
48pub struct JarImpl<C: Configuration> {
49    phantom: PhantomData<C>,
50}
51
52/// The interned ingredient hashes values of type `Data` to produce an `Id`.
53///
54/// It used to store interned structs but also to store the id fields of a tracked struct.
55/// Interned values endure until they are explicitly removed in some way.
56pub struct IngredientImpl<C: Configuration> {
57    /// Index of this ingredient in the database (used to construct database-ids, etc).
58    ingredient_index: IngredientIndex,
59
60    /// Maps from data to the existing interned id for that data.
61    ///
62    /// Deadlock requirement: We access `value_map` while holding lock on `key_map`, but not vice versa.
63    key_map: FxDashMap<C::Fields<'static>, Id>,
64
65    /// Stores the revision when this interned ingredient was last cleared.
66    /// You can clear an interned table at any point, deleting all its entries,
67    /// but that will make anything dependent on those entries dirty and in need
68    /// of being recomputed.
69    reset_at: Revision,
70}
71
72/// Struct storing the interned fields.
73pub struct Value<C>
74where
75    C: Configuration,
76{
77    fields: C::Fields<'static>,
78    memos: MemoTable,
79    syncs: SyncTable,
80}
81
82impl<C> Value<C>
83where
84    C: Configuration,
85{
86    /// Fields of this interned struct.
87    #[cfg(feature = "salsa_unstable")]
88    pub fn fields(&self) -> &C::Fields<'static> {
89        &self.fields
90    }
91}
92
93impl<C: Configuration> Default for JarImpl<C> {
94    fn default() -> Self {
95        Self {
96            phantom: PhantomData,
97        }
98    }
99}
100
101impl<C: Configuration> Jar for JarImpl<C> {
102    fn create_ingredients(
103        _zalsa: &Zalsa,
104        first_index: IngredientIndex,
105        _dependencies: IngredientIndices,
106    ) -> Vec<Box<dyn Ingredient>> {
107        vec![Box::new(IngredientImpl::<C>::new(first_index)) as _]
108    }
109
110    fn id_struct_type_id() -> TypeId {
111        TypeId::of::<C::Struct<'static>>()
112    }
113}
114
115impl<C> IngredientImpl<C>
116where
117    C: Configuration,
118{
119    pub fn new(ingredient_index: IngredientIndex) -> Self {
120        Self {
121            ingredient_index,
122            key_map: Default::default(),
123            reset_at: Revision::start(),
124        }
125    }
126
127    unsafe fn to_internal_data<'db>(&'db self, data: C::Fields<'db>) -> C::Fields<'static> {
128        unsafe { std::mem::transmute(data) }
129    }
130
131    unsafe fn from_internal_data<'db>(data: &'db C::Fields<'static>) -> &'db C::Fields<'db> {
132        unsafe { std::mem::transmute(data) }
133    }
134
135    /// Intern data to a unique reference.
136    ///
137    /// If `key` is already interned, returns the existing [`Id`] for the interned data without
138    /// invoking `assemble`.
139    /// Otherwise, invokes `assemble` with the given `key` and the [`Id`] to be allocated for this
140    /// interned value. The resulting [`C::Data`] will then be interned.
141    ///
142    /// Note: Using the database within the `assemble` function may result in a deadlock if
143    /// the database ends up trying to intern or allocate a new value.
144    pub fn intern<'db, Key>(
145        &'db self,
146        db: &'db dyn crate::Database,
147        key: Key,
148        assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
149    ) -> C::Struct<'db>
150    where
151        Key: Hash,
152        C::Fields<'db>: HashEqLike<Key>,
153    {
154        C::struct_from_id(self.intern_id(db, key, assemble))
155    }
156
157    /// Intern data to a unique reference.
158    ///
159    /// If `key` is already interned, returns the existing [`Id`] for the interned data without
160    /// invoking `assemble`.
161    /// Otherwise, invokes `assemble` with the given `key` and the [`Id`] to be allocated for this
162    /// interned value. The resulting [`C::Data`] will then be interned.
163    ///
164    /// Note: Using the database within the `assemble` function may result in a deadlock if
165    /// the database ends up trying to intern or allocate a new value.
166    pub fn intern_id<'db, Key>(
167        &'db self,
168        db: &'db dyn crate::Database,
169        key: Key,
170        assemble: impl FnOnce(Id, Key) -> C::Fields<'db>,
171    ) -> crate::Id
172    where
173        Key: Hash,
174        // We'd want the following predicate, but this currently implies `'static` due to a rustc
175        // bug
176        // for<'db> C::Data<'db>: HashEqLike<Key>,
177        // so instead we go with this and transmute the lifetime in the `eq` closure
178        C::Fields<'db>: HashEqLike<Key>,
179    {
180        let zalsa_local = db.zalsa_local();
181        zalsa_local.report_tracked_read(
182            InputDependencyIndex::for_table(self.ingredient_index),
183            Durability::MAX,
184            self.reset_at,
185            InputAccumulatedValues::Empty,
186        );
187
188        // Optimization to only get read lock on the map if the data has already been interned.
189        let data_hash = self.key_map.hasher().hash_one(&key);
190        let shard = &self.key_map.shards()[self.key_map.determine_shard(data_hash as _)];
191        let eq = |(data, _): &_| {
192            // SAFETY: it's safe to go from Data<'static> to Data<'db>
193            // shrink lifetime here to use a single lifetime in Lookup::eq(&StructKey<'db>, &C::Data<'db>)
194            let data: &C::Fields<'db> = unsafe { std::mem::transmute(data) };
195            HashEqLike::eq(data, &key)
196        };
197
198        {
199            let lock = shard.read();
200            if let Some(bucket) = lock.find(data_hash, eq) {
201                // SAFETY: Read lock on map is held during this block
202                return unsafe { *bucket.as_ref().1.get() };
203            }
204        }
205
206        let mut lock = shard.write();
207        match lock.find_or_find_insert_slot(data_hash, eq, |(element, _)| {
208            self.key_map.hasher().hash_one(element)
209        }) {
210            // Data has been interned by a racing call, use that ID instead
211            Ok(slot) => unsafe { *slot.as_ref().1.get() },
212            // We won any races so should intern the data
213            Err(slot) => {
214                let zalsa = db.zalsa();
215                let table = zalsa.table();
216                let id = zalsa_local.allocate(table, self.ingredient_index, |id| Value::<C> {
217                    fields: unsafe { self.to_internal_data(assemble(id, key)) },
218                    memos: Default::default(),
219                    syncs: Default::default(),
220                });
221                unsafe {
222                    lock.insert_in_slot(
223                        data_hash,
224                        slot,
225                        (
226                            table.get::<Value<C>>(id).fields.clone(),
227                            SharedValue::new(id),
228                        ),
229                    )
230                };
231                debug_assert_eq!(
232                    data_hash,
233                    self.key_map
234                        .hasher()
235                        .hash_one(table.get::<Value<C>>(id).fields.clone())
236                );
237                id
238            }
239        }
240    }
241
242    /// Lookup the data for an interned value based on its id.
243    /// Rarely used since end-users generally carry a struct with a pointer directly
244    /// to the interned item.
245    pub fn data<'db>(&'db self, db: &'db dyn Database, id: Id) -> &'db C::Fields<'db> {
246        let internal_data = db.zalsa().table().get::<Value<C>>(id);
247        unsafe { Self::from_internal_data(&internal_data.fields) }
248    }
249
250    /// Lookup the fields from an interned struct.
251    /// Note that this is not "leaking" since no dependency edge is required.
252    pub fn fields<'db>(&'db self, db: &'db dyn Database, s: C::Struct<'db>) -> &'db C::Fields<'db> {
253        self.data(db, C::deref_struct(s))
254    }
255
256    #[cfg(feature = "salsa_unstable")]
257    /// Returns all data corresponding to the interned struct.
258    pub fn entries<'db>(
259        &'db self,
260        db: &'db dyn crate::Database,
261    ) -> impl Iterator<Item = &'db Value<C>> {
262        db.zalsa()
263            .table()
264            .pages
265            .iter()
266            .filter_map(|(_, page)| page.cast_type::<crate::table::Page<Value<C>>>())
267            .flat_map(|page| page.slots())
268    }
269
270    pub fn reset(&mut self, revision: Revision) {
271        assert!(revision > self.reset_at);
272        self.reset_at = revision;
273        self.key_map.clear();
274    }
275}
276
277impl<C> Ingredient for IngredientImpl<C>
278where
279    C: Configuration,
280{
281    fn ingredient_index(&self) -> IngredientIndex {
282        self.ingredient_index
283    }
284
285    unsafe fn maybe_changed_after(
286        &self,
287        _db: &dyn Database,
288        _input: Id,
289        revision: Revision,
290    ) -> MaybeChangedAfter {
291        MaybeChangedAfter::from(revision < self.reset_at)
292    }
293
294    fn cycle_recovery_strategy(&self) -> crate::cycle::CycleRecoveryStrategy {
295        crate::cycle::CycleRecoveryStrategy::Panic
296    }
297
298    fn origin(&self, _db: &dyn Database, _key_index: crate::Id) -> Option<QueryOrigin> {
299        None
300    }
301
302    fn mark_validated_output(
303        &self,
304        _db: &dyn Database,
305        executor: DatabaseKeyIndex,
306        output_key: crate::Id,
307    ) {
308        unreachable!(
309            "mark_validated_output({:?}, {:?}): input cannot be the output of a tracked function",
310            executor, output_key
311        );
312    }
313
314    fn remove_stale_output(
315        &self,
316        _db: &dyn Database,
317        executor: DatabaseKeyIndex,
318        stale_output_key: crate::Id,
319    ) {
320        unreachable!(
321            "remove_stale_output({:?}, {:?}): interned ids are not outputs",
322            executor, stale_output_key
323        );
324    }
325
326    // Interned ingredients do not, normally, get deleted except when they are "reset" en masse.
327    // There ARE methods (e.g., `clear_deleted_entries` and `remove`) for deleting individual
328    // items, but those are only used for tracked struct ingredients.
329    fn requires_reset_for_new_revision(&self) -> bool {
330        false
331    }
332
333    fn fmt_index(&self, index: Option<crate::Id>, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
334        fmt_index(C::DEBUG_NAME, index, fmt)
335    }
336
337    fn debug_name(&self) -> &'static str {
338        C::DEBUG_NAME
339    }
340}
341
342impl<C> std::fmt::Debug for IngredientImpl<C>
343where
344    C: Configuration,
345{
346    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347        f.debug_struct(std::any::type_name::<Self>())
348            .field("index", &self.ingredient_index)
349            .finish()
350    }
351}
352
353impl<C> Slot for Value<C>
354where
355    C: Configuration,
356{
357    unsafe fn memos(&self, _current_revision: Revision) -> &MemoTable {
358        &self.memos
359    }
360
361    fn memos_mut(&mut self) -> &mut MemoTable {
362        &mut self.memos
363    }
364
365    unsafe fn syncs(&self, _current_revision: Revision) -> &crate::table::sync::SyncTable {
366        &self.syncs
367    }
368}
369
370/// A trait for types that hash and compare like `O`.
371pub trait HashEqLike<O> {
372    fn hash<H: Hasher>(&self, h: &mut H);
373    fn eq(&self, data: &O) -> bool;
374}
375
376/// The `Lookup` trait is a more flexible variant on [`std::borrow::Borrow`]
377/// and [`std::borrow::ToOwned`].
378///
379/// It is implemented by "some type that can be used as the lookup key for `O`".
380/// This means that `self` can be hashed and compared for equality with values
381/// of type `O` without actually creating an owned value. It `self` needs to be interned,
382/// it can be converted into an equivalent value of type `O`.
383///
384/// The canonical example is `&str: Lookup<String>`. However, this example
385/// alone can be handled by [`std::borrow::Borrow`][]. In our case, we may have
386/// multiple keys accumulated into a struct, like `ViewStruct: Lookup<(K1, ...)>`,
387/// where `struct ViewStruct<L1: Lookup<K1>...>(K1...)`. The `Borrow` trait
388/// requires that `&(K1...)` be convertible to `&ViewStruct` which just isn't
389/// possible. `Lookup` instead offers direct `hash` and `eq` methods.
390pub trait Lookup<O> {
391    fn into_owned(self) -> O;
392}
393
394impl<T> Lookup<T> for T {
395    fn into_owned(self) -> T {
396        self
397    }
398}
399
400impl<T> HashEqLike<T> for T
401where
402    T: Hash + Eq,
403{
404    fn hash<H: Hasher>(&self, h: &mut H) {
405        Hash::hash(self, &mut *h);
406    }
407
408    fn eq(&self, data: &T) -> bool {
409        self == data
410    }
411}
412
413impl<T> HashEqLike<T> for &T
414where
415    T: Hash + Eq,
416{
417    fn hash<H: Hasher>(&self, h: &mut H) {
418        Hash::hash(*self, &mut *h);
419    }
420
421    fn eq(&self, data: &T) -> bool {
422        **self == *data
423    }
424}
425
426impl<T> HashEqLike<&T> for T
427where
428    T: Hash + Eq,
429{
430    fn hash<H: Hasher>(&self, h: &mut H) {
431        Hash::hash(self, &mut *h);
432    }
433
434    fn eq(&self, data: &&T) -> bool {
435        *self == **data
436    }
437}
438
439impl<T> Lookup<T> for &T
440where
441    T: Clone,
442{
443    fn into_owned(self) -> T {
444        Clone::clone(self)
445    }
446}
447
448impl<'a, T> HashEqLike<&'a T> for Box<T>
449where
450    T: ?Sized + Hash + Eq,
451    Box<T>: From<&'a T>,
452{
453    fn hash<H: Hasher>(&self, h: &mut H) {
454        Hash::hash(self, &mut *h)
455    }
456    fn eq(&self, data: &&T) -> bool {
457        **self == **data
458    }
459}
460
461impl<'a, T> Lookup<Box<T>> for &'a T
462where
463    T: ?Sized + Hash + Eq,
464    Box<T>: From<&'a T>,
465{
466    fn into_owned(self) -> Box<T> {
467        Box::from(self)
468    }
469}
470
471impl Lookup<String> for &str {
472    fn into_owned(self) -> String {
473        self.to_owned()
474    }
475}
476impl HashEqLike<&str> for String {
477    fn hash<H: Hasher>(&self, h: &mut H) {
478        Hash::hash(self, &mut *h)
479    }
480
481    fn eq(&self, data: &&str) -> bool {
482        self == *data
483    }
484}
485
486impl<A, T: Hash + Eq + PartialEq<A>> HashEqLike<&[A]> for Vec<T> {
487    fn hash<H: Hasher>(&self, h: &mut H) {
488        Hash::hash(self, h);
489    }
490
491    fn eq(&self, data: &&[A]) -> bool {
492        self.len() == data.len() && data.iter().enumerate().all(|(i, a)| &self[i] == a)
493    }
494}
495impl<A: Hash + Eq + PartialEq<T> + Clone + Lookup<T>, T> Lookup<Vec<T>> for &[A] {
496    fn into_owned(self) -> Vec<T> {
497        self.iter().map(|a| Lookup::into_owned(a.clone())).collect()
498    }
499}
500
501impl<const N: usize, A, T: Hash + Eq + PartialEq<A>> HashEqLike<[A; N]> for Vec<T> {
502    fn hash<H: Hasher>(&self, h: &mut H) {
503        Hash::hash(self, h);
504    }
505
506    fn eq(&self, data: &[A; N]) -> bool {
507        self.len() == data.len() && data.iter().enumerate().all(|(i, a)| &self[i] == a)
508    }
509}
510impl<const N: usize, A: Hash + Eq + PartialEq<T> + Clone + Lookup<T>, T> Lookup<Vec<T>> for [A; N] {
511    fn into_owned(self) -> Vec<T> {
512        self.into_iter()
513            .map(|a| Lookup::into_owned(a.clone()))
514            .collect()
515    }
516}
517
518impl HashEqLike<&Path> for PathBuf {
519    fn hash<H: Hasher>(&self, h: &mut H) {
520        Hash::hash(self, h);
521    }
522
523    fn eq(&self, data: &&Path) -> bool {
524        self == data
525    }
526}
527impl Lookup<PathBuf> for &Path {
528    fn into_owned(self) -> PathBuf {
529        self.to_owned()
530    }
531}