arc_cell/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(feature = "const-new", feature(const_fn_trait_bound))]
3
4use std::{
5    fmt::{Debug, Formatter},
6    marker::PhantomData,
7    sync::{
8        atomic::{AtomicUsize, Ordering},
9        Arc, Weak,
10    },
11};
12
13/// Atomically swappable/clonable Arc pointer value.
14pub type ArcCell<T> = AtomicCell<Arc<T>>;
15/// Atomically swappable/clonable Weak Arc pointer value.
16pub type WeakCell<T> = AtomicCell<Weak<T>>;
17
18/// Atomically swappable/clonable/optional Arc pointer value.
19pub type OptionalArcCell<T> = AtomicCell<Option<Arc<T>>>;
20/// Atomically swappable/clonable/optional Weak Arc pointer value.
21pub type OptionalWeakCell<T> = AtomicCell<Option<Weak<T>>>;
22
23/// An atomic-based cell designed for holding Arc-style pointers.
24pub struct AtomicCell<T: AtomicCellStorable> {
25    value: AtomicUsize,
26    _marker: PhantomData<T>,
27}
28
29impl<T: AtomicCellStorable> AtomicCell<T> {
30    /// Create a new AtomicCell with the given initial value.
31    pub fn new(value: T) -> Self {
32        AtomicCell {
33            value: AtomicUsize::new(value.into_value()),
34            _marker: PhantomData,
35        }
36    }
37
38    /// Replace the value in the cell, returning the old value.
39    pub fn set(&self, value: T) -> T {
40        let old = self.internal_take();
41        self.internal_put(value);
42        old
43    }
44
45    fn internal_take(&self) -> T {
46        unsafe {
47            let mut current = self.value.load(Ordering::SeqCst);
48            T::from_value(loop {
49                // Try to take it ourselves
50                match self.value.compare_exchange_weak(
51                    current,
52                    T::TAKEN_VALUE,
53                    Ordering::SeqCst,
54                    Ordering::SeqCst,
55                ) {
56                    Ok(val) if val != T::TAKEN_VALUE => break val,
57                    Ok(_) => current = T::TAKEN_VALUE, // Someone else was working on it, retry
58                    Err(new_val) => current = new_val, // Someone got to it first, retry
59                }
60
61                // Hint to the CPU we're in a spin loop to reduce power consumption and allow
62                // another hyperthread to possibly start.
63                core::hint::spin_loop();
64            })
65        }
66    }
67
68    fn internal_put(&self, value: T) {
69        let _old = self.value.swap(value.into_value(), Ordering::SeqCst);
70        debug_assert_eq!(_old, T::TAKEN_VALUE);
71    }
72}
73
74impl<T: AtomicCellStorable> Drop for AtomicCell<T> {
75    fn drop(&mut self) {
76        unsafe {
77            let _ = T::from_value(self.value.load(Ordering::SeqCst));
78        }
79    }
80}
81
82impl<T: AtomicCellStorable + Clone> AtomicCell<T> {
83    /// Returns a clone of the stored value.
84    pub fn get(&self) -> T {
85        let value = self.internal_take();
86        let copy = value.clone();
87        self.internal_put(value);
88        copy
89    }
90}
91
92impl<T: AtomicCellStorable + Clone> Clone for AtomicCell<T> {
93    fn clone(&self) -> AtomicCell<T> {
94        AtomicCell::new(self.get())
95    }
96}
97
98impl<T: AtomicCellStorable + Default> AtomicCell<T> {
99    /// Take the value stored in the cell, replacing it with the default value.
100    pub fn take(&self) -> T {
101        // We must construct the new value first in case it panics.
102        let new_value = T::default();
103
104        let value = self.internal_take();
105        self.internal_put(new_value);
106
107        value
108    }
109}
110
111impl<T: AtomicCellStorable + Default> Default for AtomicCell<T> {
112    fn default() -> Self {
113        AtomicCell::new(T::default())
114    }
115}
116
117#[cfg(feature = "const-new")]
118impl<T: AtomicCellStorable + AtomicCellConstInit> AtomicCell<T> {
119    pub const fn const_new() -> Self {
120        AtomicCell {
121            value: AtomicUsize::new(T::DEFAULT_VALUE),
122            _marker: PhantomData,
123        }
124    }
125}
126
127impl<T> AtomicCell<Weak<T>> {
128    /// Create a new AtomicCell with an empty Weak<T> stored inside.
129    pub fn empty() -> Self {
130        AtomicCell::new(Weak::new())
131    }
132
133    /// Attempt to upgrade the Weak pointer to a strong Arc pointer.
134    pub fn upgrade(&self) -> Option<Arc<T>> {
135        self.get().upgrade()
136    }
137
138    /// Downgrade the Arc value and store it in the cell.
139    pub fn store(&self, arc: &Arc<T>) {
140        self.set(Arc::downgrade(arc));
141    }
142}
143
144impl<T> AtomicCell<Option<Weak<T>>> {
145    /// Attempt to upgrade the Weak pointer to a strong Arc pointer (if it is not None).
146    pub fn upgrade(&self) -> Option<Arc<T>> {
147        self.get().and_then(|weak| weak.upgrade())
148    }
149
150    /// Downgrade the Arc value and store it in the cell.
151    pub fn store(&self, arc: &Arc<T>) {
152        self.set(Some(Arc::downgrade(arc)));
153    }
154}
155
156impl<T: AtomicCellStorable + Clone + Debug> Debug for AtomicCell<T> {
157    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
158        fmt.debug_tuple("AtomicCell").field(&self.get()).finish()
159    }
160}
161
162/// It is up to the implementer to ensure this is safe to implement.
163///
164/// `from_value` and `into_value` should never panic nor return TAKEN_VALUE.
165/// It is also up to the implementer to ensure that if T implements Clone,
166/// its implementation of clone() will never panic.
167pub unsafe trait AtomicCellStorable {
168    /// A sentinel value that a valid instance should never occupy.
169    const TAKEN_VALUE: usize;
170    /// Convert an instance into a raw value, transferring ownership.
171    fn into_value(self) -> usize;
172    /// Convert a raw value back into an instance.
173    unsafe fn from_value(value: usize) -> Self;
174}
175
176unsafe impl<T> AtomicCellStorable for Arc<T> {
177    const TAKEN_VALUE: usize = usize::MAX;
178
179    fn into_value(self) -> usize {
180        Arc::into_raw(self) as usize
181    }
182
183    unsafe fn from_value(value: usize) -> Self {
184        Arc::from_raw(value as *const T)
185    }
186}
187
188unsafe impl<T> AtomicCellStorable for Weak<T> {
189    // This must be MAX-1 because MAX is the sentinel value Weak uses for the empty state.
190    const TAKEN_VALUE: usize = usize::MAX - 1;
191
192    fn into_value(self) -> usize {
193        Weak::into_raw(self) as usize
194    }
195
196    unsafe fn from_value(value: usize) -> Self {
197        Weak::from_raw(value as *const T)
198    }
199}
200
201const EMPTY_OPTION: usize = 0;
202
203unsafe impl<T> AtomicCellStorable for Option<Arc<T>> {
204    const TAKEN_VALUE: usize = <Arc<T> as AtomicCellStorable>::TAKEN_VALUE;
205
206    fn into_value(self) -> usize {
207        match self {
208            None => EMPTY_OPTION,
209            Some(arc) => Arc::into_raw(arc) as usize,
210        }
211    }
212
213    unsafe fn from_value(value: usize) -> Self {
214        match value {
215            EMPTY_OPTION => None,
216            value => Some(Arc::from_raw(value as *const T)),
217        }
218    }
219}
220
221unsafe impl<T> AtomicCellStorable for Option<Weak<T>> {
222    const TAKEN_VALUE: usize = <Weak<T> as AtomicCellStorable>::TAKEN_VALUE;
223
224    fn into_value(self) -> usize {
225        match self {
226            None => EMPTY_OPTION,
227            Some(arc) => Weak::into_raw(arc) as usize,
228        }
229    }
230
231    unsafe fn from_value(value: usize) -> Self {
232        match value {
233            EMPTY_OPTION => None,
234            value => Some(Weak::from_raw(value as *const T)),
235        }
236    }
237}
238
239pub unsafe trait AtomicCellConstInit {
240    const DEFAULT_VALUE: usize;
241}
242
243unsafe impl<T> AtomicCellConstInit for Option<Arc<T>> {
244    const DEFAULT_VALUE: usize = EMPTY_OPTION;
245}
246
247unsafe impl<T> AtomicCellConstInit for Option<Weak<T>> {
248    const DEFAULT_VALUE: usize = EMPTY_OPTION;
249}
250
251#[cfg(test)]
252mod tests {
253    use crate::{ArcCell, WeakCell};
254    use std::sync::{
255        atomic::{AtomicUsize, Ordering},
256        Arc,
257    };
258
259    #[test]
260    fn arc_cell() {
261        let data1 = Arc::new(5);
262        let data2 = Arc::new(6);
263
264        let cell = ArcCell::new(data1);
265        assert_eq!(*cell.get(), 5);
266        cell.set(data2);
267        assert_eq!(*cell.get(), 6);
268    }
269
270    #[test]
271    fn weak_cell() {
272        let data = Arc::new(5);
273
274        let cell = WeakCell::empty();
275        cell.store(&data);
276        assert_eq!(cell.upgrade(), Some(data.clone()));
277        drop(data);
278        assert_eq!(cell.upgrade(), None);
279    }
280
281    #[test]
282    fn cell_drops() {
283        static DROPS: AtomicUsize = AtomicUsize::new(0);
284        struct DropCount;
285        impl std::ops::Drop for DropCount {
286            fn drop(&mut self) {
287                DROPS.fetch_add(1, Ordering::SeqCst);
288            }
289        }
290        {
291            let _cell = ArcCell::new(Arc::new(DropCount));
292        }
293        assert_eq!(DROPS.load(Ordering::SeqCst), 1);
294    }
295}