sovran_arc/
arcm.rs

1use std::fmt::Debug;
2use std::sync::{Arc, Mutex, Weak};
3
4/// A wrapper combining Arc and Mutex for convenient shared mutable access
5/// Only works with types that implement Clone
6pub struct Arcm<T: Clone> {
7    inner: Arc<Mutex<T>>,
8}
9
10impl<T: Clone> Arcm<T> {
11    /// Creates a new Arcm containing the given value
12    pub fn new(value: T) -> Self {
13        Self {
14            inner: Arc::new(Mutex::new(value)),
15        }
16    }
17
18    /// Modifies the contained value using the provided closure
19    pub fn modify<F, R>(&self, f: F) -> R
20    where
21        F: FnOnce(&mut T) -> R,
22    {
23        let mut guard = self
24            .inner
25            .lock()
26            .unwrap_or_else(|poisoned| poisoned.into_inner());
27        f(&mut *guard)
28    }
29
30    /// Returns a copy of the contained value
31    pub fn value(&self) -> T {
32        match self.inner.lock() {
33            Ok(guard) => guard.clone(),
34            Err(poisoned) => poisoned.into_inner().clone(),
35        }
36    }
37
38    /// Returns a weak reference to the contained value
39    pub fn downgrade(&self) -> WeakArcm<T> {
40        WeakArcm {
41            inner: Arc::downgrade(&self.inner),
42        }
43    }
44
45    /// Replace the value without cloning the old one, returns the old value.
46    pub fn replace(&self, value: T) -> T {
47        let mut guard = match self.inner.lock() {
48            Ok(guard) => guard,
49            Err(poisoned) => poisoned.into_inner(),
50        };
51        std::mem::replace(&mut *guard, value)
52    }
53}
54
55impl<T: Clone> Clone for Arcm<T> {
56    fn clone(&self) -> Self {
57        Self {
58            inner: Arc::clone(&self.inner),
59        }
60    }
61}
62
63impl<T: Clone + Debug> Debug for Arcm<T> {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        f.debug_struct("Arcm").field("inner", &self.inner).finish()
66    }
67}
68
69impl<T: Clone + Default> Default for Arcm<T> {
70    fn default() -> Self {
71        Self::new(T::default())
72    }
73}
74
75impl<T: Clone> From<T> for Arcm<T> {
76    fn from(value: T) -> Self {
77        Self::new(value)
78    }
79}
80
81/// A weak reference wrapper for Arcm
82pub struct WeakArcm<T: Clone> {
83    inner: Weak<Mutex<T>>,
84}
85
86impl<T: Clone> WeakArcm<T> {
87    /// Attempts to modify the value if the original Arcm still exists
88    pub fn modify<F, R>(&self, f: F) -> Option<R>
89    where
90        F: FnOnce(&mut T) -> R,
91    {
92        self.inner.upgrade().map(|arc| {
93            let mut guard = arc.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
94            f(&mut *guard)
95        })
96    }
97
98    /// Attempts to get a copy of the value if the original Arcm still exists
99    pub fn value(&self) -> Option<T> {
100        self.inner.upgrade().map(|arc| match arc.lock() {
101            Ok(guard) => guard.clone(),
102            Err(poisoned) => poisoned.into_inner().clone(),
103        })
104    }
105
106    /// Attempts to replace the value if the original Arcm still exists
107    pub fn replace(&self, value: T) -> Option<T> {
108        self.inner.upgrade().map(|arc| {
109            let mut guard = arc.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
110            std::mem::replace(&mut *guard, value)
111        })
112    }
113}
114
115impl<T: Clone> Debug for WeakArcm<T> {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        f.debug_struct("WeakArcm")
118            .field("inner", &self.inner)
119            .finish()
120    }
121}
122
123// Example usage and tests
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use std::panic::{self, AssertUnwindSafe};
128    use std::thread;
129
130    #[test]
131    fn test_basic_usage() {
132        let v = Arcm::new(1);
133
134        v.modify(|v| *v = 42);
135        assert_eq!(v.value(), 42);
136    }
137
138    #[test]
139    fn test_multiple_references() {
140        let v1 = Arcm::new(1);
141        let v2 = v1.clone();
142
143        v1.modify(|v| *v = 42);
144        assert_eq!(v2.value(), 42);
145    }
146
147    #[test]
148    fn test_complex_modification() {
149        let numbers = Arcm::new(Vec::new());
150
151        numbers.modify(|v| v.push(1));
152        numbers.modify(|v| v.push(2));
153
154        assert_eq!(numbers.value(), vec![1, 2]);
155    }
156
157    #[test]
158    fn test_weak_reference() {
159        let strong = Arcm::new(42);
160        let weak = strong.downgrade();
161
162        // Much cleaner access
163        assert_eq!(weak.value(), Some(42));
164
165        // After dropping the strong reference
166        drop(strong);
167        assert_eq!(weak.value(), None);
168    }
169
170    #[test]
171    fn test_weak_modification() {
172        let strong = Arcm::new(vec![1, 2, 3]);
173        let weak = strong.downgrade();
174
175        // Modify through weak reference
176        let length = weak.modify(|v| {
177            v.push(4);
178            v.len()
179        });
180        assert_eq!(length, Some(4));
181        assert_eq!(strong.value(), vec![1, 2, 3, 4]);
182
183        // After dropping the strong reference
184        drop(strong);
185        let result = weak.modify(|v| v.push(5));
186        assert_eq!(result, None);
187    }
188
189    #[test]
190    fn test_default() {
191        // Creates an Arcm containing an empty Vec
192        let vec_arcm: Arcm<Vec<i32>> = Arcm::default();
193        assert_eq!(vec_arcm.value(), Vec::new());
194
195        // Creates an Arcm containing 0
196        let int_arcm: Arcm<i32> = Arcm::default();
197        assert_eq!(int_arcm.value(), 0);
198
199        // Creates an Arcm containing empty String
200        let string_arcm: Arcm<String> = Arcm::default();
201        assert_eq!(string_arcm.value(), String::new());
202    }
203
204    #[test]
205    fn test_from() {
206        // Using From directly
207        let arcm1 = Arcm::from(42);
208        assert_eq!(arcm1.value(), 42);
209
210        // Using Into (which is automatically implemented when From is implemented)
211        let arcm2: Arcm<String> = "hello".to_string().into();
212        assert_eq!(arcm2.value(), "hello");
213
214        // Using into() method - with explicit type annotation
215        let arcm3: Arcm<Vec<i32>> = Vec::new().into();
216        assert!(arcm3.value().is_empty());
217    }
218
219    #[test]
220    fn test_arcm_poisoned_mutex_recovery() {
221        let arcm = Arcm::new(42);
222        let arcm_clone = arcm.clone();
223
224        // Poison the mutex by causing a panic while holding the lock
225        let _ = panic::catch_unwind(AssertUnwindSafe(|| {
226            let handle = thread::spawn(move || {
227                // This will poison the mutex
228                arcm_clone.modify(|_| panic!("Deliberate panic to poison mutex"));
229            });
230
231            // Wait for the thread to complete (and panic)
232            let _ = handle.join();
233        }));
234
235        // Now try to use the poisoned mutex - should recover
236        let value = arcm.value();
237        assert_eq!(value, 42);
238
239        // Try to modify the poisoned mutex - should recover
240        let result = arcm.modify(|v| {
241            *v = 100;
242            *v
243        });
244        assert_eq!(result, 100);
245        assert_eq!(arcm.value(), 100);
246    }
247
248    #[test]
249    fn test_arcm_replace() {
250        let arcm = Arcm::new(42);
251
252        // Test basic replace functionality
253        let old_value = arcm.replace(100);
254        assert_eq!(old_value, 42);
255        assert_eq!(arcm.value(), 100);
256
257        // Test replace with multiple references
258        let arcm2 = arcm.clone();
259        arcm.replace(200);
260        assert_eq!(arcm2.value(), 200);
261
262        // Test replace with complex types
263        let vec_arcm = Arcm::new(vec![1, 2, 3]);
264        let old_vec = vec_arcm.replace(vec![4, 5, 6]);
265        assert_eq!(old_vec, vec![1, 2, 3]);
266        assert_eq!(vec_arcm.value(), vec![4, 5, 6]);
267    }
268
269    #[test]
270    fn test_arcm_replace_with_poisoned_mutex() {
271        let arcm = Arcm::new(42);
272        let arcm_clone = arcm.clone();
273
274        // Poison the mutex
275        let _ = panic::catch_unwind(AssertUnwindSafe(|| {
276            let handle = thread::spawn(move || {
277                arcm_clone.modify(|_| panic!("Deliberate panic to poison mutex"));
278            });
279            let _ = handle.join();
280        }));
281
282        // Try replace with poisoned mutex - should recover
283        let old_value = arcm.replace(100);
284        assert_eq!(old_value, 42);
285        assert_eq!(arcm.value(), 100);
286    }
287
288    #[test]
289    fn test_weak_arcm_poisoned_mutex() {
290        let strong = Arcm::new(42);
291        let weak = strong.downgrade();
292        let strong_clone = strong.clone();
293
294        // Poison the mutex
295        let _ = panic::catch_unwind(AssertUnwindSafe(|| {
296            let handle = thread::spawn(move || {
297                strong_clone.modify(|_| panic!("Deliberate panic to poison mutex"));
298            });
299            let _ = handle.join();
300        }));
301
302        // Try weak methods with poisoned mutex
303        let value = weak.value();
304        assert_eq!(value, Some(42));
305
306        let result = weak.modify(|v| {
307            *v = 100;
308            *v
309        });
310        assert_eq!(result, Some(100));
311        assert_eq!(strong.value(), 100);
312    }
313
314    #[test]
315    fn test_weak_arcm_replace() {
316        let strong = Arcm::new(42);
317        let weak = strong.downgrade();
318
319        // Test basic replace
320        let old_value = weak.replace(100);
321        assert_eq!(old_value, Some(42));
322        assert_eq!(strong.value(), 100);
323
324        // Test replace after dropping strong reference
325        drop(strong);
326        let result = weak.replace(200);
327        assert_eq!(result, None); // Should return None when strong ref is gone
328    }
329
330    #[test]
331    fn test_weak_arcm_poisoned_and_replace() {
332        let strong = Arcm::new(42);
333        let weak = strong.downgrade();
334        let strong_clone = strong.clone();
335
336        // Poison the mutex
337        let _ = panic::catch_unwind(AssertUnwindSafe(|| {
338            let handle = thread::spawn(move || {
339                strong_clone.modify(|_| panic!("Deliberate panic to poison mutex"));
340            });
341            let _ = handle.join();
342        }));
343
344        // Test replace with poisoned mutex
345        let old_value = weak.replace(100);
346        assert_eq!(old_value, Some(42));
347        assert_eq!(strong.value(), 100);
348    }
349
350    #[test]
351    fn test_arcm_thread_safety() {
352        let arcm = Arcm::new(0);
353        let threads = 10;
354        let increments_per_thread = 1000;
355
356        let handles: Vec<_> = (0..threads)
357            .map(|_| {
358                let arcm = arcm.clone();
359                thread::spawn(move || {
360                    for _ in 0..increments_per_thread {
361                        arcm.modify(|v| *v += 1);
362                    }
363                })
364            })
365            .collect();
366
367        for handle in handles {
368            handle.join().unwrap();
369        }
370
371        assert_eq!(arcm.value(), threads * increments_per_thread);
372    }
373}