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}