Skip to main content

micromegas_tracing/
static_set_cache.rs

1//! Per-callsite cache behind `key = static expr` properties.
2use crate::property_set::{Property, PropertySet, apply_pairs};
3use rustc_hash::FxBuildHasher;
4use std::{
5    collections::HashMap,
6    sync::{PoisonError, RwLock},
7};
8
9// The base set's address, then `(ptr, len)` of every value. Names are fixed per callsite, so
10// they stay out of the key; `len` keeps two substrings that share a start address apart.
11type Key<const M: usize> = (usize, [(usize, usize); M]);
12
13/// Interned sets of one callsite, one per distinct tuple of values. Entries are never removed:
14/// the cardinality contract of `PropertySet` applies.
15pub struct StaticSetCache<const M: usize> {
16    map: RwLock<HashMap<Key<M>, &'static PropertySet, FxBuildHasher>>,
17}
18
19impl<const M: usize> Default for StaticSetCache<M> {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl<const M: usize> StaticSetCache<M> {
26    pub const fn new() -> Self {
27        Self {
28            map: RwLock::new(HashMap::with_hasher(FxBuildHasher)),
29        }
30    }
31
32    /// The set made of `base` with `pairs` applied in order (case-insensitive, last wins).
33    pub fn get(
34        &self,
35        base: &'static PropertySet,
36        pairs: [(&'static str, &'static str); M],
37    ) -> &'static PropertySet {
38        let key: Key<M> = (
39            base as *const PropertySet as usize,
40            pairs.map(|(_, v)| (v.as_ptr() as usize, v.len())),
41        );
42        // Poisoning is recovered from: the map only holds `&'static` values, and an insert
43        // can't be observed half-done.
44        let hit = self
45            .map
46            .read()
47            .unwrap_or_else(PoisonError::into_inner)
48            .get(&key)
49            .copied();
50        if let Some(set) = hit {
51            return set;
52        }
53        let mut properties: Vec<Property> = base.get_properties().to_vec();
54        apply_pairs(&mut properties, pairs);
55        // Interned outside any lock of ours. Two threads missing the same key intern the same
56        // set, and the map keeps one entry.
57        let set = PropertySet::find_or_create(properties);
58        self.map
59            .write()
60            .unwrap_or_else(PoisonError::into_inner)
61            .entry(key)
62            .or_insert(set)
63    }
64}