sovran_arc/
arcmo.rs

1use std::fmt::Debug;
2use std::sync::{Arc, Mutex, Weak};
3
4/// A wrapper combining Arc and Mutex for convenient shared mutable access to optional values
5/// Only works with types that implement Clone
6pub struct Arcmo<T: Clone> {
7    inner: Arc<Mutex<Option<T>>>,
8}
9
10impl<T: Clone> Arcmo<T> {
11    /// Creates a new empty Arcmo
12    pub fn none() -> Self {
13        Self {
14            inner: Arc::new(Mutex::new(None)),
15        }
16    }
17
18    /// Creates a new Arcmo containing Some(value)
19    pub fn some(value: T) -> Self {
20        Self {
21            inner: Arc::new(Mutex::new(Some(value))),
22        }
23    }
24
25    /// Modifies the contained value using the provided closure.
26    /// If no value exists, creates one using T::Default before applying the modification.
27    /// Returns the result of the closure.
28    pub fn modify<F, R>(&self, f: F) -> R
29    where
30        T: Default,
31        F: FnOnce(&mut T) -> R,
32    {
33        let mut guard = self
34            .inner
35            .lock()
36            .unwrap_or_else(|poisoned| poisoned.into_inner());
37
38        match &mut *guard {
39            Some(value) => f(value),
40            None => {
41                let mut value = T::default();
42                let result = f(&mut value);
43                *guard = Some(value);
44                result
45            }
46        }
47    }
48
49    /// Sets the value to None and returns the previous value if it existed
50    pub fn take(&self) -> Option<T> {
51        let mut guard = self
52            .inner
53            .lock()
54            .unwrap_or_else(|poisoned| poisoned.into_inner());
55        guard.take()
56    }
57
58    /// Sets the value to Some(value) and returns the previous value if it existed
59    pub fn replace(&self, value: T) -> Option<T> {
60        let mut guard = self
61            .inner
62            .lock()
63            .unwrap_or_else(|poisoned| poisoned.into_inner());
64        guard.replace(value)
65    }
66
67    /// Returns a copy of the contained value if it exists
68    pub fn value(&self) -> Option<T> {
69        let guard = self
70            .inner
71            .lock()
72            .unwrap_or_else(|poisoned| poisoned.into_inner());
73        guard.clone()
74    }
75
76    /// Returns true if the contained value is Some
77    pub fn is_some(&self) -> bool {
78        let guard = self
79            .inner
80            .lock()
81            .unwrap_or_else(|poisoned| poisoned.into_inner());
82        guard.is_some()
83    }
84
85    /// Returns true if the contained value is None
86    pub fn is_none(&self) -> bool {
87        let guard = self
88            .inner
89            .lock()
90            .unwrap_or_else(|poisoned| poisoned.into_inner());
91        guard.is_none()
92    }
93
94    /// Returns a weak reference to the contained value
95    pub fn downgrade(&self) -> WeakArcmo<T> {
96        WeakArcmo {
97            inner: Arc::downgrade(&self.inner),
98        }
99    }
100}
101
102impl<T: Clone> Clone for Arcmo<T> {
103    fn clone(&self) -> Self {
104        Self {
105            inner: Arc::clone(&self.inner),
106        }
107    }
108}
109
110impl<T: Clone + Debug> Debug for Arcmo<T> {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        f.debug_struct("Arcmo").field("inner", &self.inner).finish()
113    }
114}
115
116impl<T: Clone + Default> Default for Arcmo<T> {
117    fn default() -> Self {
118        Self::none()
119    }
120}
121
122/// A weak reference wrapper for Arcmo
123pub struct WeakArcmo<T: Clone> {
124    inner: Weak<Mutex<Option<T>>>,
125}
126
127impl<T: Clone> WeakArcmo<T> {
128    /// Attempts to modify the value if it exists and the original Arcmo still exists
129    pub fn modify<F, R>(&self, f: F) -> Option<R>
130    where
131        T: Default,
132        F: FnOnce(&mut T) -> R,
133    {
134        self.inner.upgrade().map(|arc| {
135            let mut guard = arc.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
136            match &mut *guard {
137                Some(value) => f(value),
138                None => {
139                    let mut value = T::default();
140                    let result = f(&mut value);
141                    *guard = Some(value);
142                    result
143                }
144            }
145        })
146    }
147
148    /// Attempts to get a copy of the value if it exists and the original Arcmo still exists
149    pub fn value(&self) -> Option<T> {
150        self.inner.upgrade().and_then(|arc| match arc.lock() {
151            Ok(guard) => guard.clone(),
152            Err(poisoned) => poisoned.into_inner().clone(),
153        })
154    }
155
156    /// Returns true if both the original Arcmo exists and contains Some value
157    pub fn is_some(&self) -> bool {
158        self.inner
159            .upgrade()
160            .map(|arc| match arc.lock() {
161                Ok(guard) => guard.is_some(),
162                Err(poisoned) => poisoned.into_inner().is_some(),
163            })
164            .unwrap_or(false)
165    }
166
167    /// Returns true if either the original Arcmo is dropped or contains None
168    pub fn is_none(&self) -> bool {
169        !self.is_some()
170    }
171
172    /// Attempts to replace the value if the original Arcmo still exists
173    pub fn replace(&self, value: T) -> Option<Option<T>> {
174        self.inner.upgrade().map(|arc| {
175            let mut guard = arc.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
176            std::mem::replace(&mut *guard, Some(value))
177        })
178    }
179}
180
181impl<T: Clone> Debug for WeakArcmo<T> {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        f.debug_struct("WeakArcmo")
184            .field("inner", &self.inner)
185            .finish()
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192    use std::panic::{self, AssertUnwindSafe};
193    use std::thread;
194
195    #[derive(Clone, Debug, Default, PartialEq)]
196    struct Settings {
197        enabled: bool,
198        count: i32,
199        name: String,
200    }
201
202    impl Settings {
203        fn update_timestamp(&mut self) {
204            self.count += 1;
205        }
206
207        fn recalculate_dependencies(&mut self) {
208            if self.enabled {
209                self.count *= 2;
210            }
211        }
212    }
213
214    #[test]
215    fn test_default() {
216        let arcmo: Arcmo<Vec<i32>> = Arcmo::default();
217        assert!(arcmo.is_none());
218
219        let int_arcmo: Arcmo<i32> = Arcmo::default();
220        assert!(int_arcmo.is_none());
221    }
222
223    #[test]
224    fn test_basic_usage() {
225        let v = Arcmo::some(1);
226        let result = v.modify(|v| {
227            *v = 42;
228            *v
229        });
230        assert_eq!(result, 42);
231        assert_eq!(v.value(), Some(42));
232    }
233
234    #[test]
235    fn test_modification_patterns() {
236        // Set whole struct
237        let state = Arcmo::none();
238        let new_state = Settings {
239            enabled: true,
240            count: 42,
241            name: "test".to_string(),
242        };
243        let result = state.modify(|s: &mut Settings| {
244            *s = new_state.clone();
245            s.count
246        });
247        assert_eq!(result, 42);
248        assert_eq!(state.value(), Some(new_state));
249
250        // Update one field
251        let counter = Arcmo::<Settings>::none();
252        let result = counter.modify(|s: &mut Settings| {
253            s.count += 1;
254            s.count
255        });
256        assert_eq!(result, 1);
257        assert_eq!(counter.value().unwrap().count, 1);
258
259        // Call methods
260        let vec = Arcmo::<Vec<i32>>::none();
261        let len = vec.modify(|v: &mut Vec<i32>| {
262            v.push(42);
263            v.len()
264        });
265        assert_eq!(len, 1);
266        assert_eq!(vec.value(), Some(vec![42]));
267
268        // Complex updates
269        let settings = Arcmo::<Settings>::none();
270        let count = settings.modify(|s: &mut Settings| {
271            s.enabled = true;
272            s.update_timestamp();
273            s.recalculate_dependencies();
274            s.count
275        });
276        assert_eq!(count, 2); // 1 from timestamp, then doubled
277        let result = settings.value().unwrap();
278        assert!(result.enabled);
279        assert_eq!(result.count, 2);
280    }
281
282    #[test]
283    fn test_take_and_replace() {
284        let v = Arcmo::some(1);
285        assert_eq!(v.take(), Some(1));
286        assert!(v.is_none());
287        assert_eq!(v.replace(42), None);
288        assert_eq!(v.value(), Some(42));
289    }
290
291    #[test]
292    fn test_multiple_references() {
293        let v1 = Arcmo::some(1);
294        let v2 = v1.clone();
295
296        let result = v1.modify(|v| {
297            *v = 42;
298            *v
299        });
300        assert_eq!(result, 42);
301        assert_eq!(v2.value(), Some(42));
302
303        v1.take();
304        assert!(v2.is_none());
305    }
306
307    #[test]
308    fn test_modify_none_uses_default() {
309        let settings: Arcmo<Settings> = Arcmo::none();
310
311        let count = settings.modify(|s: &mut Settings| {
312            assert!(!s.enabled);
313            assert_eq!(s.count, 0);
314            assert_eq!(s.name, "");
315            s.enabled = true;
316            s.count
317        });
318
319        assert_eq!(count, 0);
320        let result = settings.value().unwrap();
321        assert!(result.enabled);
322        assert_eq!(result.count, 0);
323        assert_eq!(result.name, "");
324    }
325
326    #[test]
327    fn test_weak_reference() {
328        let strong = Arcmo::some(42);
329        let weak = strong.downgrade();
330        assert_eq!(weak.value(), Some(42));
331        drop(strong);
332        assert_eq!(weak.value(), None);
333    }
334
335    #[test]
336    fn test_weak_with_none() {
337        let strong = Arcmo::none();
338        let weak = strong.downgrade();
339
340        assert_eq!(weak.value(), None);
341        assert!(weak.is_none());
342        assert!(!weak.is_some());
343
344        strong.replace(42);
345        assert_eq!(weak.value(), Some(42));
346        assert!(!weak.is_none());
347        assert!(weak.is_some());
348
349        strong.take();
350        assert_eq!(weak.value(), None);
351        assert!(weak.is_none());
352        assert!(!weak.is_some());
353    }
354
355    #[test]
356    fn test_weak_modification() {
357        let strong = Arcmo::some(vec![1, 2, 3]);
358        let weak = strong.downgrade();
359
360        // Modify through weak reference
361        let length = weak.modify(|v| {
362            v.push(4);
363            v.len()
364        });
365        assert_eq!(length, Some(4));
366        assert_eq!(strong.value(), Some(vec![1, 2, 3, 4]));
367
368        // Test with None -> Default
369        let strong = Arcmo::<Vec<i32>>::none();
370        let weak = strong.downgrade();
371        let len = weak.modify(|v| {
372            v.push(42);
373            v.len()
374        });
375        assert_eq!(len, Some(1));
376        assert_eq!(strong.value(), Some(vec![42]));
377
378        // After dropping the strong reference
379        drop(strong);
380        let result = weak.modify(|v| {
381            v.push(5);
382            v.len()
383        });
384        assert_eq!(result, None);
385    }
386
387    #[test]
388    fn test_arcmo_poisoned_mutex_recovery() {
389        let arcmo = Arcmo::some(42);
390        let arcmo_clone = arcmo.clone();
391
392        // Poison the mutex by causing a panic while holding the lock
393        let _ = panic::catch_unwind(AssertUnwindSafe(|| {
394            let handle = thread::spawn(move || {
395                // This will poison the mutex
396                arcmo_clone.modify(|_| panic!("Deliberate panic to poison mutex"));
397            });
398
399            // Wait for the thread to complete (and panic)
400            let _ = handle.join();
401        }));
402
403        // Now try to use the poisoned mutex - should recover
404        let value = arcmo.value();
405        assert_eq!(value, Some(42));
406
407        // Try to modify the poisoned mutex - should recover
408        arcmo.modify(|v| *v = 100);
409        assert_eq!(arcmo.value(), Some(100));
410
411        // Test other methods with poisoned mutex
412        assert!(arcmo.is_some());
413        assert!(!arcmo.is_none());
414
415        let taken = arcmo.take();
416        assert_eq!(taken, Some(100));
417        assert!(arcmo.is_none());
418
419        let replaced = arcmo.replace(200);
420        assert_eq!(replaced, None);
421        assert_eq!(arcmo.value(), Some(200));
422    }
423
424    #[test]
425    fn test_weak_arcmo_replace() {
426        // Test with Some value
427        let strong = Arcmo::some(42);
428        let weak = strong.downgrade();
429
430        let prev_value = weak.replace(100);
431        assert_eq!(prev_value, Some(Some(42))); // Previous value was Some(42)
432        assert_eq!(strong.value(), Some(100)); // New value is 100
433
434        // Test with None value
435        let strong_none = Arcmo::<i32>::none();
436        let weak_none = strong_none.downgrade();
437
438        let prev_value = weak_none.replace(200);
439        assert_eq!(prev_value, Some(None)); // Previous value was None
440        assert_eq!(strong_none.value(), Some(200)); // New value is 200
441
442        // Test with dropped strong reference
443        drop(strong_none);
444        let result = weak_none.replace(300);
445        assert_eq!(result, None); // Should return None when strong ref is gone
446    }
447
448    #[test]
449    fn test_weak_arcmo_poisoned_mutex() {
450        let strong = Arcmo::some(42);
451        let weak = strong.downgrade();
452        let strong_clone = strong.clone();
453
454        // Poison the mutex
455        let _ = panic::catch_unwind(AssertUnwindSafe(|| {
456            let handle = thread::spawn(move || {
457                strong_clone.modify(|_| panic!("Deliberate panic to poison mutex"));
458            });
459            let _ = handle.join();
460        }));
461
462        // Try weak methods with poisoned mutex
463        let value = weak.value();
464        assert_eq!(value, Some(42));
465
466        assert!(weak.is_some());
467        assert!(!weak.is_none());
468
469        let result = weak.modify(|v| {
470            *v = 100;
471            *v
472        });
473        assert_eq!(result, Some(100));
474        assert_eq!(strong.value(), Some(100));
475
476        // Test replace with poisoned mutex
477        let old_value = weak.replace(200);
478        assert_eq!(old_value, Some(Some(100)));
479        assert_eq!(strong.value(), Some(200));
480    }
481
482    #[test]
483    fn test_weak_arcmo_none_to_some() {
484        // Test upgrading None to Some via replace
485        let strong = Arcmo::<i32>::none();
486        let weak = strong.downgrade();
487
488        assert!(strong.is_none());
489        assert!(weak.is_none());
490
491        // Replace None with Some
492        let prev = weak.replace(42);
493        assert_eq!(prev, Some(None));
494        assert!(strong.is_some());
495        assert!(weak.is_some());
496        assert_eq!(strong.value(), Some(42));
497    }
498}