basin2_lib/ilib/
atomic_set.rs

1use std::ops::Deref;
2use std::fmt::{Debug, self};
3use std::sync::atomic::{ AtomicPtr, Ordering };
4use std::ptr::null_mut;
5
6// once-settable, any-readonly-gettable option
7// useful for read-only structs with after-initialization, one-time fill-in of data
8pub struct AtomicSet<T: Send + Sync + Sized> {
9    item: AtomicPtr<T>,
10}
11
12impl<T: Send + Sync + Sized> Default for AtomicSet<T> {
13    fn default() -> AtomicSet<T> {
14        AtomicSet::new()
15    }
16}
17
18impl<T: Send + Sync + Sized> Deref for AtomicSet<T> {
19    type Target = T;
20    fn deref(&self) -> &T {
21        self.get()
22    }
23}
24
25impl<T: Debug + Send + Sync + Sized> Debug for AtomicSet<T> {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        (**self).fmt(f)
28    }
29}
30
31impl<T: Clone + Send + Sync + Sized> Clone for AtomicSet<T> {
32    fn clone(&self) -> Self {
33        let ptr = self.item.load(Ordering::Relaxed);
34        let ptr = if ptr == null_mut::<T>() {
35            ptr
36        } else {
37            Box::into_raw(Box::new(unsafe { ptr.as_ref() }.unwrap().clone()))
38        };
39        AtomicSet {
40            item: AtomicPtr::new(ptr),
41        }
42    }
43}
44
45impl<T: PartialEq + Send + Sync + Sized> PartialEq for AtomicSet<T> {
46    fn eq(&self, other: &Self) -> bool {
47        return **self == **other;
48    }
49}
50
51impl<T: Send + Sync + Sized> Drop for AtomicSet<T> {
52    fn drop(&mut self) {
53        let ptr = self.item.load(Ordering::Relaxed);
54        if ptr != null_mut::<T>() {
55            drop(unsafe { Box::from_raw(ptr) });
56        }
57    }
58}
59
60impl<T: Send + Sync + Sized> AtomicSet<T> {
61    
62    pub fn new() -> AtomicSet<T> {
63        AtomicSet {
64            item: AtomicPtr::new(null_mut::<T>()),
65        }
66    }
67
68    pub fn try_set(&self, item: T) -> bool {
69        let boxed_item = Box::into_raw(Box::new(item));
70        let null_ptr = null_mut::<T>();
71        if self.item.compare_and_swap(null_ptr, boxed_item, Ordering::Relaxed) != null_ptr {
72            drop(unsafe { Box::from_raw(boxed_item) });
73            return false;
74        }
75        return true;
76    }
77
78    pub fn set(&self, item: T) {
79        let boxed_item = Box::into_raw(Box::new(item));
80        let null_ptr = null_mut::<T>();
81        if self.item.compare_and_swap(null_ptr, boxed_item, Ordering::Relaxed) != null_ptr {
82            drop(unsafe { Box::from_raw(boxed_item) });
83            panic!("attempted to re-set AtomicSet!");
84        }
85    }
86
87    pub fn get(&self) -> &T {
88        let ptr = self.item.load(Ordering::Relaxed);
89        if ptr == null_mut::<T>() {
90            panic!("attempt to get value of unset AtomicSet!");
91        }
92        unsafe { ptr.as_ref() }.unwrap()
93    }
94
95    pub fn is_set(&self) -> bool {
96        let ptr = self.item.load(Ordering::Relaxed);
97        ptr != null_mut::<T>()
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_can_set() {
107        let setter = AtomicSet::<u32>::new();
108        assert_eq!(setter.is_set(), false);
109        setter.set(12345);
110        assert_eq!(setter.is_set(), true);
111
112        assert_eq!(*setter.get(), 12345);
113        assert_eq!(*setter.get(), 12345);
114        assert_eq!(*setter, 12345);
115        assert_eq!(setter.is_set(), true);
116    }
117
118    #[test]
119    #[should_panic]
120    fn test_cannot_set_twice() {
121        let setter = AtomicSet::<u32>::new();
122        setter.set(12345);
123        setter.set(12345);
124    }
125}