use std::borrow::Borrow;
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash};
use std::fmt;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::atomic::{AtomicUsize, Ordering};
use lru_cache::LruCache;
pub struct ThreadSafeCache<K, V, S = RandomState>
where K: Eq + Hash, S: BuildHasher
{
inner: Mutex<LruCache<K, Arc<V>, S>>,
hits: AtomicUsize,
misses: AtomicUsize,
}
impl<K: Eq + Hash, V> ThreadSafeCache<K, V> {
#[inline]
pub fn new(capacity: usize) -> Self {
Self::with_hasher(capacity, RandomState::new())
}
}
impl<K, V, S> ThreadSafeCache<K, V, S>
where K: Eq + Hash, S: BuildHasher
{
pub fn with_hasher(capacity: usize, hasher: S) -> Self {
ThreadSafeCache{
inner: Mutex::new(LruCache::with_hasher(capacity, hasher)),
hits: AtomicUsize::new(0),
misses: AtomicUsize::new(0),
}
}
#[doc(hidden)]
fn lock(&self) -> MutexGuard<LruCache<K, Arc<V>, S>> {
self.inner.lock().expect("ThreadSafeCache mutex poisoned")
}
}
#[allow(dead_code)]
impl<K: Eq + Hash, V> ThreadSafeCache<K, V> {
pub fn contains_key<Q>(&self, key: &Q) -> bool
where K: Borrow<Q>, Q: ?Sized + Eq + Hash
{
let y = self.lock().contains_key(key);
if !y { self.miss(); }
y
}
pub fn get<Q>(&self, key: &Q) -> Option<Arc<V>>
where K: Borrow<Q>, Q: ?Sized + Eq + Hash
{
match self.lock().get_mut(key) {
Some(v) => { self.hit(); Some(v.clone()) }
None => { self.miss(); None }
}
}
pub fn put(&self, k: K, v: V) -> Arc<V> {
let value = Arc::new(v);
self.lock().insert(k, value.clone()).unwrap_or_else(|| value)
}
pub fn insert(&self, k: K, v: V) -> Option<Arc<V>> {
self.lock().insert(k, Arc::new(v))
}
pub fn remove<Q>(&self, key: &Q) -> Option<Arc<V>>
where K: Borrow<Q>, Q: ?Sized + Eq + Hash
{
match self.lock().remove(key) {
r @ Some(_) => { self.hit(); r }
r @ None => { self.miss(); r }
}
}
pub fn capacity(&self) -> usize {
self.lock().capacity()
}
pub fn set_capacity(&self, capacity: usize) {
self.lock().set_capacity(capacity);
}
pub fn remove_lru(&self) -> Option<(K, Arc<V>)> {
self.lock().remove_lru()
}
pub fn len(&self) -> usize {
self.lock().len()
}
pub fn is_empty(&self) -> bool {
self.lock().is_empty()
}
pub fn clear(&self) {
self.lock().clear()
}
}
impl<K: Eq + Hash, V> ThreadSafeCache<K, V> {
fn hit(&self) -> usize {
let inc = 1;
self.hits.fetch_add(inc, Ordering::Relaxed) + inc
}
fn miss(&self) -> usize {
let inc = 1;
self.misses.fetch_add(inc, Ordering::Relaxed) + inc
}
}
impl<K :Eq + Hash, V> ThreadSafeCache<K, V> {
pub fn hits(&self) -> usize {
self.hits.load(Ordering::Relaxed)
}
pub fn misses(&self) -> usize {
self.misses.load(Ordering::Relaxed)
}
}
impl<K: Eq + Hash, V> fmt::Debug for ThreadSafeCache<K, V> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut ds = fmt.debug_struct("ThreadSafeCache");
if let Ok(inner) = self.inner.try_lock() {
ds.field("capacity", &inner.capacity());
ds.field("len", &inner.len());
}
ds.finish()
}
}