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

View File

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

View File

@ -111,7 +111,7 @@ impl SystemWorld {
/// Reset the compilation state in preparation of a new compilation.
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();
}
self.now.take();

View File

@ -24,7 +24,6 @@ chinese-number = { workspace = true }
ciborium = { workspace = true }
comemo = { workspace = true }
csv = { workspace = true }
dashmap = { workspace = true }
ecow = { workspace = true}
fontdb = { workspace = true }
hayagriva = { workspace = true }
@ -37,7 +36,6 @@ icu_segmenter = { workspace = true }
image = { workspace = true }
indexmap = { workspace = true }
kurbo = { workspace = true }
lasso = { workspace = true }
lipsum = { workspace = true }
log = { 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::hash::Hash;
use std::num::NonZeroUsize;
use std::sync::RwLock;
use comemo::Prehashed;
use dashmap::DashMap;
use ecow::{eco_format, EcoVec};
use indexmap::IndexMap;
use smallvec::SmallVec;
@ -32,7 +32,7 @@ pub struct Introspector {
/// even if all top-level queries are distinct, they often have shared
/// subqueries. Example: Individual counter queries with `before` that
/// all depend on a global counter query.
queries: DashMap<u128, EcoVec<Prehashed<Content>>>,
queries: QueryCache,
}
impl Introspector {
@ -118,8 +118,8 @@ impl Introspector {
/// Query for all matching elements.
pub fn query(&self, selector: &Selector) -> EcoVec<Prehashed<Content>> {
let hash = crate::util::hash128(selector);
if let Some(output) = self.queries.get(&hash) {
return output.clone();
if let Some(output) = self.queries.get(hash) {
return output;
}
let output = match selector {
@ -256,7 +256,7 @@ impl Default for Introspector {
elems: IndexMap::new(),
labels: HashMap::new(),
page_numberings: vec![],
queries: DashMap::new(),
queries: QueryCache::default(),
}
}
}
@ -266,3 +266,27 @@ impl Debug for 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);
// 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.
Location { hash, disambiguator, variant: 0 }
@ -78,7 +78,7 @@ impl<'a> Locator<'a> {
match item {
FrameItem::Group(group) => self.visit_frame(&group.frame),
FrameItem::Meta(Meta::Elem(elem), _) => {
let mut hashes = self.hashes.borrow_mut();
let hashes = self.hashes.get_mut();
let loc = elem.location().unwrap();
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::sync::RwLock;
use ecow::EcoString;
use lasso::{Spur, ThreadedRodeo};
use once_cell::sync::Lazy;
use crate::foundations::cast;
/// 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.
///
@ -16,22 +24,30 @@ static INTERNER: Lazy<ThreadedRodeo> = Lazy::new(ThreadedRodeo::new);
/// 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.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PicoStr(Spur);
pub struct PicoStr(u32);
impl PicoStr {
/// Creates a new interned string.
pub fn new(s: impl AsRef<str>) -> Self {
Self(INTERNER.get_or_intern(s.as_ref()))
}
pub fn new(string: &str) -> Self {
if let Some(&id) = INTERNER.read().unwrap().to_id.get(string) {
return id;
}
/// Creates a new interned string from a static string.
pub fn static_(s: &'static str) -> Self {
Self(INTERNER.get_or_intern_static(s))
let mut interner = INTERNER.write().unwrap();
let num = interner.from_id.len().try_into().expect("out of string ids");
// 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.
pub fn resolve(&self) -> &'static str {
INTERNER.resolve(&self.0)
INTERNER.read().unwrap().from_id[self.0 as usize]
}
}