Skip to main content

oxistore_cache/
sync.rs

1//! Thread-safe cache wrapper backed by a [`std::sync::Mutex`].
2//!
3//! [`SyncCache`] wraps any [`Cache`] implementation and provides a
4//! `Send + Sync` interface suitable for sharing between threads.  The
5//! internal mutex is a standard library `Mutex`, so poisoning is reported as
6//! a panic — a poisoned mutex indicates a bug (panic) in another thread, not
7//! a recoverable error.
8//!
9//! # Example
10//!
11//! ```rust
12//! use oxistore_cache::{LruCache, SyncCache};
13//! use std::sync::Arc;
14//!
15//! let cache = Arc::new(SyncCache::new(LruCache::<i32, i32>::new(100)));
16//! cache.put(1, 42);
17//! assert_eq!(cache.get(&1), Some(42));
18//! ```
19
20use std::sync::Mutex;
21
22use crate::Cache;
23
24/// A thread-safe wrapper around any [`Cache`] implementation.
25///
26/// Wraps `C` (which implements `Cache<K, V>`) in a [`Mutex`], providing
27/// `Send + Sync` access from multiple threads.  All methods acquire the
28/// mutex for their duration.
29///
30/// # Panics
31///
32/// Every method panics if the inner mutex is poisoned, which happens when
33/// another thread panicked while holding the lock.
34pub struct SyncCache<K, V, C: Cache<K, V>> {
35    inner: Mutex<C>,
36    _marker: std::marker::PhantomData<(K, V)>,
37}
38
39impl<K, V, C: Cache<K, V>> SyncCache<K, V, C> {
40    /// Wrap `cache` in a `SyncCache`.
41    pub fn new(cache: C) -> Self {
42        Self {
43            inner: Mutex::new(cache),
44            _marker: std::marker::PhantomData,
45        }
46    }
47
48    /// Look up `key`, cloning and returning the value if present.
49    ///
50    /// This acquires the lock, calls [`Cache::get`] (which may update
51    /// recency), and clones the result so the lock can be released.
52    pub fn get(&self, key: &K) -> Option<V>
53    where
54        V: Clone,
55    {
56        self.inner.lock().expect("mutex poisoned").get(key).cloned()
57    }
58
59    /// Insert or update `key` -> `value`.
60    ///
61    /// Returns the evicted value (if any) per the underlying cache policy.
62    pub fn put(&self, key: K, value: V) -> Option<V> {
63        self.inner.lock().expect("mutex poisoned").put(key, value)
64    }
65
66    /// Remove the entry for `key`, returning its value if present.
67    pub fn remove(&self, key: &K) -> Option<V> {
68        self.inner.lock().expect("mutex poisoned").remove(key)
69    }
70
71    /// Return the number of live entries in the cache.
72    pub fn len(&self) -> usize {
73        self.inner.lock().expect("mutex poisoned").len()
74    }
75
76    /// Return `true` if the cache holds no entries.
77    pub fn is_empty(&self) -> bool {
78        self.len() == 0
79    }
80
81    /// Remove all entries from the cache.
82    pub fn clear(&self) {
83        self.inner.lock().expect("mutex poisoned").clear();
84    }
85}