8303: Allow interning strings r=jonas-schievink a=jonas-schievink

We don't use it yet, that's a bit more complicated.

bors r+

Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
bors[bot] 2021-04-03 00:09:41 +00:00 committed by GitHub
commit eb264fb819
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,17 +3,20 @@
//! Eventually this should probably be replaced with salsa-based interning. //! Eventually this should probably be replaced with salsa-based interning.
use std::{ use std::{
collections::HashMap,
fmt::{self, Debug}, fmt::{self, Debug},
hash::{BuildHasherDefault, Hash}, hash::{BuildHasherDefault, Hash},
ops::Deref, ops::Deref,
sync::Arc, sync::Arc,
}; };
use dashmap::{DashMap, SharedValue}; use dashmap::{lock::RwLockWriteGuard, DashMap, SharedValue};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use rustc_hash::FxHasher; use rustc_hash::FxHasher;
type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>; type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
type Guard<T> =
RwLockWriteGuard<'static, HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>>;
#[derive(Hash)] #[derive(Hash)]
pub struct Interned<T: Internable + ?Sized> { pub struct Interned<T: Internable + ?Sized> {
@ -22,10 +25,22 @@ pub struct Interned<T: Internable + ?Sized> {
impl<T: Internable> Interned<T> { impl<T: Internable> Interned<T> {
pub fn new(obj: T) -> Self { pub fn new(obj: T) -> Self {
match Interned::lookup(&obj) {
Ok(this) => this,
Err(shard) => {
let arc = Arc::new(obj);
Self::alloc(arc, shard)
}
}
}
}
impl<T: Internable + ?Sized> Interned<T> {
fn lookup(obj: &T) -> Result<Self, Guard<T>> {
let storage = T::storage().get(); let storage = T::storage().get();
let shard_idx = storage.determine_map(&obj); let shard_idx = storage.determine_map(obj);
let shard = &storage.shards()[shard_idx]; let shard = &storage.shards()[shard_idx];
let mut shard = shard.write(); let shard = shard.write();
// Atomically, // Atomically,
// - check if `obj` is already in the map // - check if `obj` is already in the map
@ -34,13 +49,15 @@ impl<T: Internable> Interned<T> {
// This needs to be atomic (locking the shard) to avoid races with other thread, which could // This needs to be atomic (locking the shard) to avoid races with other thread, which could
// insert the same object between us looking it up and inserting it. // insert the same object between us looking it up and inserting it.
// FIXME: avoid double lookup by using raw entry API (once stable, or when hashbrown can be // FIXME: avoid double lookup/hashing by using raw entry API (once stable, or when
// plugged into dashmap) // hashbrown can be plugged into dashmap)
if let Some((arc, _)) = shard.get_key_value(&obj) { match shard.get_key_value(obj) {
return Self { arc: arc.clone() }; Some((arc, _)) => Ok(Self { arc: arc.clone() }),
None => Err(shard),
}
} }
let arc = Arc::new(obj); fn alloc(arc: Arc<T>, mut shard: Guard<T>) -> Self {
let arc2 = arc.clone(); let arc2 = arc.clone();
shard.insert(arc2, SharedValue::new(())); shard.insert(arc2, SharedValue::new(()));
@ -49,6 +66,18 @@ impl<T: Internable> Interned<T> {
} }
} }
impl Interned<str> {
pub fn new_str(s: &str) -> Self {
match Interned::lookup(s) {
Ok(this) => this,
Err(shard) => {
let arc = Arc::<str>::from(s);
Self::alloc(arc, shard)
}
}
}
}
impl<T: Internable + ?Sized> Drop for Interned<T> { impl<T: Internable + ?Sized> Drop for Interned<T> {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
@ -98,6 +127,14 @@ impl<T: Internable> PartialEq for Interned<T> {
impl<T: Internable> Eq for Interned<T> {} impl<T: Internable> Eq for Interned<T> {}
impl PartialEq for Interned<str> {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.arc, &other.arc)
}
}
impl Eq for Interned<str> {}
impl<T: Internable + ?Sized> AsRef<T> for Interned<T> { impl<T: Internable + ?Sized> AsRef<T> for Interned<T> {
#[inline] #[inline]
fn as_ref(&self) -> &T { fn as_ref(&self) -> &T {
@ -157,4 +194,4 @@ macro_rules! impl_internable {
)+ }; )+ };
} }
impl_internable!(crate::type_ref::TypeRef, crate::type_ref::TraitRef, crate::path::ModPath); impl_internable!(crate::type_ref::TypeRef, crate::type_ref::TraitRef, crate::path::ModPath, str);