Skip to main content

stratum_apps/
sync.rs

1use std::{
2    hash::Hash,
3    sync::{Arc, Mutex, MutexGuard, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},
4};
5
6use dashmap::DashMap;
7
8type SharedLockResult<'a, T, R> = Result<R, PoisonError<MutexGuard<'a, T>>>;
9type SharedRwReadResult<'a, T, R> = Result<R, PoisonError<RwLockReadGuard<'a, T>>>;
10type SharedRwWriteResult<'a, T, R> = Result<R, PoisonError<RwLockWriteGuard<'a, T>>>;
11
12/// Thread-safe shared mutable value using `Mutex` for exclusive access.
13#[derive(Debug)]
14pub struct SharedLock<T>(Arc<Mutex<T>>);
15
16impl<T> Clone for SharedLock<T> {
17    fn clone(&self) -> Self {
18        SharedLock(Arc::clone(&self.0))
19    }
20}
21
22impl<T> SharedLock<T> {
23    /// Create a new shared value.
24    pub fn new(v: T) -> Self {
25        SharedLock(Arc::new(Mutex::new(v)))
26    }
27
28    /// Execute a closure with mutable access to the inner value.
29    ///
30    /// Caution: `f` runs while the mutex is held. Avoid re-entering this
31    /// `SharedLock` from inside the closure.
32    pub fn with<F, R>(&self, f: F) -> SharedLockResult<'_, T, R>
33    where
34        F: FnOnce(&mut T) -> R,
35    {
36        let mut lock = self.0.lock()?;
37        Ok(f(&mut *lock))
38    }
39
40    /// Get a cloned snapshot of the value.
41    pub fn get(&self) -> SharedLockResult<'_, T, T>
42    where
43        T: Clone,
44    {
45        self.with(|v| v.clone())
46    }
47
48    /// Replace the inner value.
49    pub fn set(&self, value: T) -> SharedLockResult<'_, T, ()> {
50        self.with(|v| *v = value)?;
51        Ok(())
52    }
53}
54
55/// Thread-safe shared value using `RwLock` for concurrent reads and exclusive writes.
56#[derive(Debug)]
57pub struct SharedRw<T>(Arc<RwLock<T>>);
58
59impl<T> Clone for SharedRw<T> {
60    fn clone(&self) -> Self {
61        SharedRw(Arc::clone(&self.0))
62    }
63}
64
65impl<T> SharedRw<T> {
66    /// Create a new shared value.
67    pub fn new(v: T) -> Self {
68        SharedRw(Arc::new(RwLock::new(v)))
69    }
70
71    /// Execute a closure with read-only access.
72    ///
73    /// Caution: `f` runs while the read lock is held. Avoid re-entering this
74    /// `SharedRw` from inside the closure, especially for writes.
75    pub fn read<F, R>(&self, f: F) -> SharedRwReadResult<'_, T, R>
76    where
77        F: FnOnce(&T) -> R,
78    {
79        let guard = self.0.read()?;
80        Ok(f(&*guard))
81    }
82
83    /// Execute a closure with mutable access.
84    ///
85    /// Caution: `f` runs while the write lock is held. Avoid re-entering this
86    /// `SharedRw` from inside the closure.
87    pub fn write<F, R>(&self, f: F) -> SharedRwWriteResult<'_, T, R>
88    where
89        F: FnOnce(&mut T) -> R,
90    {
91        let mut guard = self.0.write()?;
92        Ok(f(&mut *guard))
93    }
94
95    /// Get a cloned snapshot of the value.
96    pub fn get(&self) -> SharedRwReadResult<'_, T, T>
97    where
98        T: Clone,
99    {
100        self.read(|v| v.clone())
101    }
102
103    /// Replace the inner value.
104    pub fn set(&self, value: T) -> SharedRwWriteResult<'_, T, ()> {
105        self.write(|v| *v = value)?;
106        Ok(())
107    }
108}
109
110/// Concurrent map wrapper over `DashMap` providing ergonomic scoped access.
111#[derive(Debug)]
112pub struct SharedMap<K: Eq + Clone + Hash, V>(Arc<DashMap<K, V>>);
113
114impl<K: Eq + Clone + Hash, V> Clone for SharedMap<K, V> {
115    fn clone(&self) -> Self {
116        SharedMap(Arc::clone(&self.0))
117    }
118}
119
120impl<K: Eq + Hash + Clone, V> SharedMap<K, V> {
121    /// Create a new concurrent map.
122    pub fn new() -> Self {
123        SharedMap(Arc::new(DashMap::new()))
124    }
125
126    /// Get an owned clone of a value.
127    ///
128    /// This releases the map entry guard immediately, so the entry may be removed
129    /// or replaced while the caller is still using the clone. Use this only when
130    /// stale clones are acceptable. Prefer [`Self::with`] or [`Self::with_mut`]
131    /// when later work depends on the entry still being present.
132    pub fn get_cloned(&self, key: &K) -> Option<V>
133    where
134        V: Clone,
135    {
136        self.0.get(key).map(|refc| refc.value().clone())
137    }
138
139    /// Read a value for a key using a closure.
140    ///
141    /// Caution: `f` runs while the entry guard is held. Avoid re-entering this
142    /// `SharedMap` from inside the closure.
143    pub fn with<F, R>(&self, key: &K, f: F) -> Option<R>
144    where
145        F: FnOnce(&V) -> R,
146    {
147        let guard = self.0.get(key)?;
148        let result = f(guard.value());
149        drop(guard);
150        Some(result)
151    }
152
153    /// Mutate a value for a key using a closure.
154    ///
155    /// Caution: `f` runs while the entry guard is held. Avoid re-entering this
156    /// `SharedMap` from inside the closure.
157    pub fn with_mut<F, R>(&self, key: &K, f: F) -> Option<R>
158    where
159        F: FnOnce(&mut V) -> R,
160    {
161        let mut guard = self.0.get_mut(key)?;
162        let result = f(guard.value_mut());
163        Some(result)
164    }
165
166    /// Mutate an entry, inserting a value produced by `default` when absent.
167    pub fn with_mut_or_insert_with<F, D, R>(&self, key: K, default: D, f: F) -> R
168    where
169        F: FnOnce(&mut V) -> R,
170        D: FnOnce() -> V,
171    {
172        let mut entry = self.0.entry(key).or_insert_with(default);
173        f(entry.value_mut())
174    }
175
176    /// Mutate an entry, inserting its default value when absent.
177    pub fn with_mut_or_default<F, R>(&self, key: K, f: F) -> R
178    where
179        V: Default,
180        F: FnOnce(&mut V) -> R,
181    {
182        self.with_mut_or_insert_with(key, V::default, f)
183    }
184
185    /// Iterate over all entries immutably.
186    ///
187    /// Caution: `f` runs while an iterator entry guard is held. Avoid
188    /// re-entering this `SharedMap` from inside the closure.
189    pub fn for_each<F, Ret>(&self, mut f: F)
190    where
191        F: FnMut(K, &V) -> Ret,
192    {
193        for entry in self.0.iter() {
194            f(entry.key().clone(), entry.value());
195        }
196    }
197
198    /// Iterate over all entries mutably.
199    ///
200    /// Caution: `f` runs while an iterator entry guard is held. Avoid
201    /// re-entering this `SharedMap` from inside the closure.
202    pub fn for_each_mut<F, Ret>(&self, mut f: F)
203    where
204        F: FnMut(K, &mut V) -> Ret,
205    {
206        for mut entry in self.0.iter_mut() {
207            f(entry.key().clone(), entry.value_mut());
208        }
209    }
210
211    /// Fallible mutable iteration over all entries.
212    ///
213    /// Caution: `f` runs while an iterator entry guard is held. Avoid
214    /// re-entering this `SharedMap` from inside the closure.
215    pub fn try_for_each_mut<F, E>(&self, mut f: F) -> Result<(), E>
216    where
217        F: FnMut(K, &mut V) -> Result<(), E>,
218    {
219        for mut entry in self.0.iter_mut() {
220            f(entry.key().clone(), entry.value_mut())?;
221        }
222        Ok(())
223    }
224
225    /// Fallible iteration over all entries.
226    ///
227    /// Caution: `f` runs while an iterator entry guard is held. Avoid
228    /// re-entering this `SharedMap` from inside the closure.
229    pub fn try_for_each<F, E>(&self, mut f: F) -> Result<(), E>
230    where
231        F: FnMut(K, &V) -> Result<(), E>,
232    {
233        for entry in self.0.iter() {
234            f(entry.key().clone(), entry.value())?;
235        }
236        Ok(())
237    }
238
239    /// Insert a key-value pair.
240    pub fn insert(&self, key: K, value: V) -> Option<V> {
241        self.0.insert(key, value)
242    }
243
244    /// Remove a key.
245    pub fn remove(&self, key: &K) -> Option<(K, V)> {
246        self.0.remove(key)
247    }
248
249    /// Check if a key exists.
250    pub fn contains_key(&self, key: &K) -> bool {
251        self.0.contains_key(key)
252    }
253
254    /// Retain entries matching predicate.
255    ///
256    /// Caution: `f` runs while `DashMap` is mutating internal shards. Avoid
257    /// re-entering this `SharedMap` from inside the predicate.
258    pub fn retain<F>(&self, f: F)
259    where
260        F: FnMut(&K, &mut V) -> bool,
261    {
262        self.0.retain(f);
263    }
264
265    /// Collect all keys.
266    pub fn keys(&self) -> Vec<K>
267    where
268        K: Clone,
269    {
270        self.0.iter().map(|e| e.key().clone()).collect()
271    }
272
273    /// Number of entries.
274    pub fn len(&self) -> usize {
275        self.0.len()
276    }
277
278    /// Check if empty.
279    pub fn is_empty(&self) -> bool {
280        self.0.is_empty()
281    }
282
283    /// Clears the collection.
284    pub fn clear(&self) {
285        self.0.clear()
286    }
287}
288
289impl<K: Eq + Hash + Clone, V> Default for SharedMap<K, V> {
290    fn default() -> Self {
291        Self::new()
292    }
293}
294
295#[cfg(test)]
296mod tests {
297    use super::*;
298
299    #[test]
300    fn shared_basic_usage() {
301        let v = SharedLock::new(10);
302
303        let _ = v.with(|x| *x += 5);
304        assert_eq!(v.get().unwrap(), 15);
305
306        let _ = v.set(42);
307        assert_eq!(v.get().unwrap(), 42);
308    }
309
310    #[test]
311    fn shared_rw_usage() {
312        let v = SharedRw::new(100);
313
314        let a = v.read(|x| *x).unwrap();
315        let b = v.read(|x| *x).unwrap();
316        assert_eq!(a, b);
317
318        let _ = v.write(|x| *x += 1);
319        assert_eq!(v.get().unwrap(), 101);
320    }
321
322    #[test]
323    fn shared_map_usage() {
324        let map = SharedMap::new();
325
326        map.insert("a", 1);
327        map.insert("b", 2);
328
329        let val = map.with(&"a", |v| *v).unwrap();
330        assert_eq!(val, 1);
331
332        map.with_mut(&"a", |v| *v += 10);
333        assert_eq!(map.with(&"a", |v| *v).unwrap(), 11);
334
335        map.with_mut_or_default("c", |v| *v += 3);
336        assert_eq!(map.get_cloned(&"c"), Some(3));
337
338        let mut sum = 0;
339        map.for_each(|_, v| sum += v);
340        assert_eq!(sum, 16);
341
342        map.remove(&"a");
343        assert!(!map.contains_key(&"a"));
344    }
345}