pingora_cache/
hashtable.rsuse lru::LruCache;
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::collections::HashMap;
pub struct ConcurrentHashTable<V, const N: usize> {
tables: [RwLock<HashMap<u128, V>>; N],
}
#[inline]
fn get_shard(key: u128, n_shards: usize) -> usize {
(key % n_shards as u128) as usize
}
impl<V, const N: usize> ConcurrentHashTable<V, N>
where
[RwLock<HashMap<u128, V>>; N]: Default,
{
pub fn new() -> Self {
ConcurrentHashTable {
tables: Default::default(),
}
}
pub fn get(&self, key: u128) -> &RwLock<HashMap<u128, V>> {
&self.tables[get_shard(key, N)]
}
#[allow(dead_code)]
pub fn read(&self, key: u128) -> RwLockReadGuard<HashMap<u128, V>> {
self.get(key).read()
}
pub fn write(&self, key: u128) -> RwLockWriteGuard<HashMap<u128, V>> {
self.get(key).write()
}
}
impl<V, const N: usize> Default for ConcurrentHashTable<V, N>
where
[RwLock<HashMap<u128, V>>; N]: Default,
{
fn default() -> Self {
Self::new()
}
}
#[doc(hidden)] pub struct LruShard<V>(RwLock<LruCache<u128, V>>);
impl<V> Default for LruShard<V> {
fn default() -> Self {
LruShard(RwLock::new(LruCache::unbounded()))
}
}
pub struct ConcurrentLruCache<V, const N: usize> {
lrus: [LruShard<V>; N],
}
impl<V, const N: usize> ConcurrentLruCache<V, N>
where
[LruShard<V>; N]: Default,
{
pub fn new(shard_capacity: usize) -> Self {
use std::num::NonZeroUsize;
const ONE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1) };
let mut cache = ConcurrentLruCache {
lrus: Default::default(),
};
for lru in &mut cache.lrus {
lru.0
.write()
.resize(shard_capacity.try_into().unwrap_or(ONE));
}
cache
}
pub fn get(&self, key: u128) -> &RwLock<LruCache<u128, V>> {
&self.lrus[get_shard(key, N)].0
}
#[allow(dead_code)]
pub fn read(&self, key: u128) -> RwLockReadGuard<LruCache<u128, V>> {
self.get(key).read()
}
pub fn write(&self, key: u128) -> RwLockWriteGuard<LruCache<u128, V>> {
self.get(key).write()
}
}