Drop dependency on DashMap

DashMap doesn't work in multi-threaded WebAssembly in Safari: https://bugs.webkit.org/show_bug.cgi?id=265581
This commit is contained in:
Laurenz 2023-11-30 18:31:56 +01:00
parent de40124adb
commit f16a9ea9ad
7 changed files with 58 additions and 44 deletions

22
Cargo.lock generated
View File

@ -781,15 +781,6 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.3" version = "0.14.3"
@ -1231,17 +1222,6 @@ dependencies = [
"arrayvec", "arrayvec",
] ]
[[package]]
name = "lasso"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4644821e1c3d7a560fe13d842d13f587c07348a1a05d3a797152d41c90c56df2"
dependencies = [
"ahash",
"dashmap",
"hashbrown 0.13.2",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -2644,7 +2624,6 @@ dependencies = [
"ciborium", "ciborium",
"comemo", "comemo",
"csv", "csv",
"dashmap",
"ecow", "ecow",
"fontdb", "fontdb",
"hayagriva", "hayagriva",
@ -2657,7 +2636,6 @@ dependencies = [
"image", "image",
"indexmap 2.1.0", "indexmap 2.1.0",
"kurbo", "kurbo",
"lasso",
"lipsum", "lipsum",
"log", "log",
"once_cell", "once_cell",

View File

@ -38,7 +38,6 @@ clap_mangen = "0.2.10"
codespan-reporting = "0.11" codespan-reporting = "0.11"
comemo = "0.3.1" comemo = "0.3.1"
csv = "1" csv = "1"
dashmap = "5.5"
dirs = "5" dirs = "5"
ecow = { version = "0.2", features = ["serde"] } ecow = { version = "0.2", features = ["serde"] }
env_proxy = "0.4" env_proxy = "0.4"
@ -59,7 +58,6 @@ include_dir = "0.7"
indexmap = { version = "2", features = ["serde"] } indexmap = { version = "2", features = ["serde"] }
inferno = "0.11.15" inferno = "0.11.15"
kurbo = "0.9" kurbo = "0.9"
lasso = { version = "0.7.2", features = ["ahasher", "multi-threaded"] }
lipsum = "0.9" lipsum = "0.9"
log = "0.4" log = "0.4"
miniz_oxide = "0.7" miniz_oxide = "0.7"

View File

@ -111,7 +111,7 @@ impl SystemWorld {
/// Reset the compilation state in preparation of a new compilation. /// Reset the compilation state in preparation of a new compilation.
pub fn reset(&mut self) { pub fn reset(&mut self) {
for slot in self.slots.borrow_mut().values_mut() { for slot in self.slots.get_mut().values_mut() {
slot.reset(); slot.reset();
} }
self.now.take(); self.now.take();

View File

@ -24,7 +24,6 @@ chinese-number = { workspace = true }
ciborium = { workspace = true } ciborium = { workspace = true }
comemo = { workspace = true } comemo = { workspace = true }
csv = { workspace = true } csv = { workspace = true }
dashmap = { workspace = true }
ecow = { workspace = true} ecow = { workspace = true}
fontdb = { workspace = true } fontdb = { workspace = true }
hayagriva = { workspace = true } hayagriva = { workspace = true }
@ -37,7 +36,6 @@ icu_segmenter = { workspace = true }
image = { workspace = true } image = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
kurbo = { workspace = true } kurbo = { workspace = true }
lasso = { workspace = true }
lipsum = { workspace = true } lipsum = { workspace = true }
log = { workspace = true } log = { workspace = true }
once_cell = { workspace = true } once_cell = { workspace = true }

View File

@ -2,9 +2,9 @@ use std::collections::{BTreeSet, HashMap};
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::hash::Hash; use std::hash::Hash;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use std::sync::RwLock;
use comemo::Prehashed; use comemo::Prehashed;
use dashmap::DashMap;
use ecow::{eco_format, EcoVec}; use ecow::{eco_format, EcoVec};
use indexmap::IndexMap; use indexmap::IndexMap;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -32,7 +32,7 @@ pub struct Introspector {
/// even if all top-level queries are distinct, they often have shared /// even if all top-level queries are distinct, they often have shared
/// subqueries. Example: Individual counter queries with `before` that /// subqueries. Example: Individual counter queries with `before` that
/// all depend on a global counter query. /// all depend on a global counter query.
queries: DashMap<u128, EcoVec<Prehashed<Content>>>, queries: QueryCache,
} }
impl Introspector { impl Introspector {
@ -118,8 +118,8 @@ impl Introspector {
/// Query for all matching elements. /// Query for all matching elements.
pub fn query(&self, selector: &Selector) -> EcoVec<Prehashed<Content>> { pub fn query(&self, selector: &Selector) -> EcoVec<Prehashed<Content>> {
let hash = crate::util::hash128(selector); let hash = crate::util::hash128(selector);
if let Some(output) = self.queries.get(&hash) { if let Some(output) = self.queries.get(hash) {
return output.clone(); return output;
} }
let output = match selector { let output = match selector {
@ -256,7 +256,7 @@ impl Default for Introspector {
elems: IndexMap::new(), elems: IndexMap::new(),
labels: HashMap::new(), labels: HashMap::new(),
page_numberings: vec![], page_numberings: vec![],
queries: DashMap::new(), queries: QueryCache::default(),
} }
} }
} }
@ -266,3 +266,27 @@ impl Debug for Introspector {
f.pad("Introspector(..)") f.pad("Introspector(..)")
} }
} }
/// Caches queries.
#[derive(Default)]
struct QueryCache(RwLock<HashMap<u128, EcoVec<Prehashed<Content>>>>);
impl QueryCache {
fn get(&self, hash: u128) -> Option<EcoVec<Prehashed<Content>>> {
self.0.read().unwrap().get(&hash).cloned()
}
fn insert(&self, hash: u128, output: EcoVec<Prehashed<Content>>) {
self.0.write().unwrap().insert(hash, output);
}
fn clear(&mut self) {
self.0.get_mut().unwrap().clear();
}
}
impl Clone for QueryCache {
fn clone(&self) -> Self {
Self(RwLock::new(self.0.read().unwrap().clone()))
}
}

View File

@ -66,7 +66,7 @@ impl<'a> Locator<'a> {
let disambiguator = self.disambiguator_impl(hash); let disambiguator = self.disambiguator_impl(hash);
// Bump the next disambiguator up by one. // Bump the next disambiguator up by one.
self.hashes.borrow_mut().insert(hash, disambiguator + 1); self.hashes.get_mut().insert(hash, disambiguator + 1);
// Create the location in its default variant. // Create the location in its default variant.
Location { hash, disambiguator, variant: 0 } Location { hash, disambiguator, variant: 0 }
@ -78,7 +78,7 @@ impl<'a> Locator<'a> {
match item { match item {
FrameItem::Group(group) => self.visit_frame(&group.frame), FrameItem::Group(group) => self.visit_frame(&group.frame),
FrameItem::Meta(Meta::Elem(elem), _) => { FrameItem::Meta(Meta::Elem(elem), _) => {
let mut hashes = self.hashes.borrow_mut(); let hashes = self.hashes.get_mut();
let loc = elem.location().unwrap(); let loc = elem.location().unwrap();
let entry = hashes.entry(loc.hash).or_default(); let entry = hashes.entry(loc.hash).or_default();

View File

@ -1,13 +1,21 @@
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::sync::RwLock;
use ecow::EcoString; use ecow::EcoString;
use lasso::{Spur, ThreadedRodeo};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::foundations::cast; use crate::foundations::cast;
/// The global string interner. /// The global string interner.
static INTERNER: Lazy<ThreadedRodeo> = Lazy::new(ThreadedRodeo::new); static INTERNER: Lazy<RwLock<Interner>> =
Lazy::new(|| RwLock::new(Interner { to_id: HashMap::new(), from_id: Vec::new() }));
/// A string interner.
struct Interner {
to_id: HashMap<&'static str, PicoStr>,
from_id: Vec<&'static str>,
}
/// An interned string. /// An interned string.
/// ///
@ -16,22 +24,30 @@ static INTERNER: Lazy<ThreadedRodeo> = Lazy::new(ThreadedRodeo::new);
/// unnecessarily. For this reason, the user should use the [`PicoStr::resolve`] /// unnecessarily. For this reason, the user should use the [`PicoStr::resolve`]
/// method to get the underlying string, such that the lookup is done only once. /// method to get the underlying string, such that the lookup is done only once.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PicoStr(Spur); pub struct PicoStr(u32);
impl PicoStr { impl PicoStr {
/// Creates a new interned string. /// Creates a new interned string.
pub fn new(s: impl AsRef<str>) -> Self { pub fn new(string: &str) -> Self {
Self(INTERNER.get_or_intern(s.as_ref())) if let Some(&id) = INTERNER.read().unwrap().to_id.get(string) {
return id;
} }
/// Creates a new interned string from a static string. let mut interner = INTERNER.write().unwrap();
pub fn static_(s: &'static str) -> Self { let num = interner.from_id.len().try_into().expect("out of string ids");
Self(INTERNER.get_or_intern_static(s))
// Create a new entry forever by leaking the string. PicoStr is only
// used for strings that aren't created en masse, so it is okay.
let id = Self(num);
let string = Box::leak(string.to_string().into_boxed_str());
interner.to_id.insert(string, id);
interner.from_id.push(string);
id
} }
/// Resolves the interned string. /// Resolves the interned string.
pub fn resolve(&self) -> &'static str { pub fn resolve(&self) -> &'static str {
INTERNER.resolve(&self.0) INTERNER.read().unwrap().from_id[self.0 as usize]
} }
} }