use dashmap::DashMap;
use once_cell::sync::Lazy;
use parking_lot::{Mutex, MutexGuard};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use super::Collection;
const STRIPES: usize = 256;
pub(crate) struct StripeSet {
keys: [Mutex<()>; STRIPES],
uniques: [Mutex<()>; STRIPES],
}
impl StripeSet {
fn new() -> Self {
Self {
keys: std::array::from_fn(|_| Mutex::new(())),
uniques: std::array::from_fn(|_| Mutex::new(())),
}
}
}
static STRIPE_SETS: Lazy<DashMap<u64, &'static StripeSet>> = Lazy::new(DashMap::new);
pub(crate) struct WriteGuard {
_guards: Vec<MutexGuard<'static, ()>>,
}
fn hash_of<T: Hash + ?Sized>(seed: u64, value: &T) -> u64 {
let mut h = DefaultHasher::new();
seed.hash(&mut h);
value.hash(&mut h);
h.finish()
}
fn lock_sorted(
family: &'static [Mutex<()>; STRIPES],
mut idx: Vec<usize>,
) -> Vec<MutexGuard<'static, ()>> {
idx.sort_unstable();
idx.dedup();
idx.into_iter().map(|i| family[i].lock()).collect()
}
impl Collection {
fn stripe_set(&self) -> &'static StripeSet {
let id = hash_of(
std::sync::Arc::as_ptr(&self.db) as usize as u64,
self.name.as_str(),
);
if let Some(set) = STRIPE_SETS.get(&id) {
return *set;
}
*STRIPE_SETS
.entry(id)
.or_insert_with(|| Box::leak(Box::new(StripeSet::new())))
}
pub(crate) fn lock_keys<'a, I>(&self, keys: I) -> WriteGuard
where
I: IntoIterator<Item = &'a str>,
{
let set = self.stripe_set();
let idx = keys
.into_iter()
.map(|k| (hash_of(0, k) as usize) % STRIPES)
.collect();
WriteGuard {
_guards: lock_sorted(&set.keys, idx),
}
}
pub(crate) fn lock_unique_tokens<'a, I>(&self, tokens: I) -> WriteGuard
where
I: IntoIterator<Item = &'a String>,
{
let set = self.stripe_set();
let idx: Vec<usize> = tokens
.into_iter()
.map(|t| (hash_of(1, t.as_str()) as usize) % STRIPES)
.collect();
if idx.is_empty() {
return WriteGuard {
_guards: Vec::new(),
};
}
WriteGuard {
_guards: lock_sorted(&set.uniques, idx),
}
}
}