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.
111pub struct SharedMap<K: Eq + Clone + Hash, V>(Arc<DashMap<K, V>>);
112
113impl<K: Eq + Clone + Hash, V> Clone for SharedMap<K, V> {
114    fn clone(&self) -> Self {
115        SharedMap(Arc::clone(&self.0))
116    }
117}
118
119impl<K: Eq + Hash + Clone, V> SharedMap<K, V> {
120    /// Create a new concurrent map.
121    pub fn new() -> Self {
122        SharedMap(Arc::new(DashMap::new()))
123    }
124
125    /// Read a value for a key using a closure.
126    ///
127    /// Caution: `f` runs while the entry guard is held. Avoid re-entering this
128    /// `SharedMap` from inside the closure.
129    pub fn with<F, R>(&self, key: &K, f: F) -> Option<R>
130    where
131        F: FnOnce(&V) -> R,
132    {
133        let guard = self.0.get(key)?;
134        let result = f(guard.value());
135        drop(guard);
136        Some(result)
137    }
138
139    /// Mutate 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_mut<F, R>(&self, key: &K, f: F) -> Option<R>
144    where
145        F: FnOnce(&mut V) -> R,
146    {
147        let mut guard = self.0.get_mut(key)?;
148        let result = f(guard.value_mut());
149        Some(result)
150    }
151
152    /// Iterate over all entries immutably.
153    ///
154    /// Caution: `f` runs while an iterator entry guard is held. Avoid
155    /// re-entering this `SharedMap` from inside the closure.
156    pub fn for_each<F, Ret>(&self, mut f: F)
157    where
158        F: FnMut(K, &V) -> Ret,
159    {
160        for entry in self.0.iter() {
161            f(entry.key().clone(), entry.value());
162        }
163    }
164
165    /// Iterate over all entries mutably.
166    ///
167    /// Caution: `f` runs while an iterator entry guard is held. Avoid
168    /// re-entering this `SharedMap` from inside the closure.
169    pub fn for_each_mut<F, Ret>(&self, mut f: F)
170    where
171        F: FnMut(K, &mut V) -> Ret,
172    {
173        for mut entry in self.0.iter_mut() {
174            f(entry.key().clone(), entry.value_mut());
175        }
176    }
177
178    /// Fallible mutable iteration over all entries.
179    ///
180    /// Caution: `f` runs while an iterator entry guard is held. Avoid
181    /// re-entering this `SharedMap` from inside the closure.
182    pub fn try_for_each_mut<F, E>(&self, mut f: F) -> Result<(), E>
183    where
184        F: FnMut(K, &mut V) -> Result<(), E>,
185    {
186        for mut entry in self.0.iter_mut() {
187            f(entry.key().clone(), entry.value_mut())?;
188        }
189        Ok(())
190    }
191
192    /// Fallible iteration over all entries.
193    ///
194    /// Caution: `f` runs while an iterator entry guard is held. Avoid
195    /// re-entering this `SharedMap` from inside the closure.
196    pub fn try_for_each<F, E>(&self, f: F) -> Result<(), E>
197    where
198        F: Fn(K, &V) -> Result<(), E>,
199    {
200        for entry in self.0.iter() {
201            f(entry.key().clone(), entry.value())?;
202        }
203        Ok(())
204    }
205
206    /// Insert a key-value pair.
207    pub fn insert(&self, key: K, value: V) -> Option<V> {
208        self.0.insert(key, value)
209    }
210
211    /// Remove a key.
212    pub fn remove(&self, key: &K) -> Option<(K, V)> {
213        self.0.remove(key)
214    }
215
216    /// Check if a key exists.
217    pub fn contains_key(&self, key: &K) -> bool {
218        self.0.contains_key(key)
219    }
220
221    /// Retain entries matching predicate.
222    ///
223    /// Caution: `f` runs while `DashMap` is mutating internal shards. Avoid
224    /// re-entering this `SharedMap` from inside the predicate.
225    pub fn retain<F>(&self, f: F)
226    where
227        F: FnMut(&K, &mut V) -> bool,
228    {
229        self.0.retain(f);
230    }
231
232    /// Collect all keys.
233    pub fn keys(&self) -> Vec<K>
234    where
235        K: Clone,
236    {
237        self.0.iter().map(|e| e.key().clone()).collect()
238    }
239
240    /// Number of entries.
241    pub fn len(&self) -> usize {
242        self.0.len()
243    }
244
245    /// Check if empty.
246    pub fn is_empty(&self) -> bool {
247        self.0.is_empty()
248    }
249}
250
251impl<K: Eq + Hash + Clone, V> Default for SharedMap<K, V> {
252    fn default() -> Self {
253        Self::new()
254    }
255}
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260
261    #[test]
262    fn shared_basic_usage() {
263        let v = SharedLock::new(10);
264
265        let _ = v.with(|x| *x += 5);
266        assert_eq!(v.get().unwrap(), 15);
267
268        let _ = v.set(42);
269        assert_eq!(v.get().unwrap(), 42);
270    }
271
272    #[test]
273    fn shared_rw_usage() {
274        let v = SharedRw::new(100);
275
276        let a = v.read(|x| *x).unwrap();
277        let b = v.read(|x| *x).unwrap();
278        assert_eq!(a, b);
279
280        let _ = v.write(|x| *x += 1);
281        assert_eq!(v.get().unwrap(), 101);
282    }
283
284    #[test]
285    fn shared_map_usage() {
286        let map = SharedMap::new();
287
288        map.insert("a", 1);
289        map.insert("b", 2);
290
291        let val = map.with(&"a", |v| *v).unwrap();
292        assert_eq!(val, 1);
293
294        map.with_mut(&"a", |v| *v += 10);
295        assert_eq!(map.with(&"a", |v| *v).unwrap(), 11);
296
297        let mut sum = 0;
298        map.for_each(|_, v| sum += v);
299        assert_eq!(sum, 13);
300
301        map.remove(&"a");
302        assert!(!map.contains_key(&"a"));
303    }
304}