Skip to main content

arcshift/
cell.rs

1use crate::ArcShift;
2use core::cell::{Cell, UnsafeCell};
3use core::fmt::{Debug, Display, Formatter};
4use core::marker::PhantomData;
5use core::ops::Deref;
6
7/// ArcShiftCell is like an ArcShift, except that it can be reloaded
8/// without requiring 'mut'-access.
9/// However, it is not 'Sync'.
10///
11/// It does not implement 'Deref', but does implement a [`ArcShiftCell::borrow`]-method which returns,
12/// a non-threadsafe `ArcShiftCellHandle<T>`. This handle implements Deref, giving access
13/// to the pointed to &T. This handle should not be leaked,
14/// but if it is leaked, the effect is that whatever value the ArcShiftCell-instance
15/// pointed to at that time, will forever leak also.
16pub struct ArcShiftCell<T: 'static + ?Sized> {
17    // UnsafeCell makes ArcShiftCell invariant, which it needs to be
18    inner: UnsafeCell<ArcShift<T>>,
19    recursion: Cell<usize>,
20}
21
22/// A handle to the pointed-to value of a ArcShiftCell. This handle should not be leaked,
23/// but if it is leaked, the effect is that whatever value the ArcShiftCell-instance
24/// pointed to at that time, will forever leak also. All the linked-list nodes from
25/// that entry and onward will also leak. So make sure to not leak the handle!
26///
27/// Leaking the handle does not cause unsoundness and is not UB.
28pub struct ArcShiftCellHandle<'a, T: 'static + ?Sized> {
29    cell: &'a ArcShiftCell<T>,
30    // Make sure ArcShiftCellHandle is neither Sync nor Send, and also invariant
31    _marker: PhantomData<*mut T>,
32}
33
34impl<T: 'static + ?Sized> Drop for ArcShiftCellHandle<'_, T> {
35    fn drop(&mut self) {
36        let mut rec = self.cell.recursion.get();
37        rec -= 1;
38        if rec == 0 {
39            // SAFETY:
40            // There are no references to 'inner', so it's safe to obtain
41            // a mutable reference.
42            unsafe { (*self.cell.inner.get()).reload() };
43        }
44        self.cell.recursion.set(rec);
45    }
46}
47
48impl<T: 'static + ?Sized> Deref for ArcShiftCellHandle<'_, T> {
49    type Target = T;
50
51    fn deref(&self) -> &Self::Target {
52        if self.cell.recursion.get() == 1 {
53            // SAFETY:
54            // We're the only owner of the ArcShiftCell, and can thus get mutable access.
55            let inner: &mut ArcShift<T> = unsafe { &mut *self.cell.inner.get() };
56            inner.get()
57        } else {
58            // SAFETY:
59            // Shared access to this UnsafeCell is always allowed.
60            // Actual mutable references to the 'inner' never live long enough
61            // to be visible by the user of this module.
62            let inner: &ArcShift<T> = unsafe { &*self.cell.inner.get() };
63            inner.shared_non_reloading_get()
64        }
65    }
66}
67
68/// ArcShiftCell cannot be Sync, but there's nothing stopping it from being Send.
69///
70/// Note that ArcShiftCell *cannot* be Sync, because then multiple threads
71/// could call 'get' simultaneously, corrupting the (non-atomic) refcount.
72// SAFETY:
73// As long as the contents of the cell are not !Send, it is safe to
74// send the cell. The object must be uniquely owned to be sent, and
75// this is only possible if we're not in a recursive call to
76// 'get'. And in this case, the properties of ArcShiftCell are the same
77// as ArcShift, and ArcShift is Send (if T is Send + Sync).
78//
79unsafe impl<T: 'static> Send for ArcShiftCell<T> where T: Send + Sync {}
80
81impl<T: 'static + ?Sized> Clone for ArcShiftCell<T> {
82    fn clone(&self) -> Self {
83        // SAFETY:
84        // Accessing the inner value is safe, since we know no other thread
85        // can be interacting with it. And even if we're in a recursive call to
86        // 'get', we're not mutating the value and cloning it is thus perfectly safe.
87        let clone = unsafe { &mut *self.inner.get() }.clone();
88        ArcShiftCell {
89            inner: UnsafeCell::new(clone),
90            recursion: Cell::new(0),
91        }
92    }
93}
94
95/// Error type representing the case that an operation was attempted from within
96/// a 'get'-method closure.
97pub struct RecursionDetected;
98
99impl Debug for RecursionDetected {
100    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
101        write!(f, "RecursionDetected")
102    }
103}
104
105impl Display for RecursionDetected {
106    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
107        write!(f, "RecursionDetected")
108    }
109}
110
111impl<T: 'static> ArcShiftCell<T> {
112    /// Create a new ArcShiftCell with the given value.
113    pub fn new(value: T) -> ArcShiftCell<T> {
114        ArcShiftCell::from_arcshift(ArcShift::new(value))
115    }
116}
117impl<T: 'static + ?Sized> ArcShiftCell<T> {
118    /// Creates an ArcShiftCell from an ArcShift-instance.
119    /// The payload is not cloned, the two pointers keep pointing to the same object.
120    pub fn from_arcshift(input: ArcShift<T>) -> ArcShiftCell<T> {
121        ArcShiftCell {
122            inner: UnsafeCell::new(input),
123            recursion: Cell::new(0),
124        }
125    }
126
127    /// Get a handle to the pointed-to T.
128    ///
129    /// Make sure not to leak this handle: See further documentation on
130    /// [`ArcShiftCellHandle`]. Leaking the handle will leak resources, but
131    /// not cause undefined behaviour.
132    #[inline]
133    pub fn borrow(&self) -> ArcShiftCellHandle<'_, T> {
134        self.recursion.set(self.recursion.get() + 1);
135        ArcShiftCellHandle {
136            cell: self,
137            _marker: PhantomData,
138        }
139    }
140
141    /// Get the value pointed to.
142    ///
143    /// This method is very fast, basically the speed of a regular reference, unless
144    /// the value has been modified by calling one of the update-methods.
145    ///
146    /// This method will do a reload (drop older values which are no longer needed).
147    ///
148    /// This method is reentrant - you are allowed to call it from within the closure 'f'.
149    /// However, only the outermost invocation will cause a reload.
150    #[inline]
151    pub fn get<R>(&self, f: impl FnOnce(&T) -> R) -> R {
152        self.recursion.set(self.recursion.get() + 1);
153        let val = if self.recursion.get() == 1 {
154            // SAFETY:
155            // Getting the inner value is safe, no other thread can be accessing it now
156            unsafe { &mut *self.inner.get() }.get()
157        } else {
158            // SAFETY:
159            // Getting the inner value is safe, no other thread can be accessing it now
160            unsafe { &*self.inner.get() }.shared_non_reloading_get()
161        };
162        let t = f(val);
163        self.recursion.set(self.recursion.get() - 1);
164        t
165    }
166
167    /// Assign the given ArcShift to this instance.
168    /// This does not copy the value T, it replaces the ArcShift instance of Self
169    /// with a clone of 'other'. It does not clone T, only the ArcShift holding it.
170    ///
171    /// This returns Err if recursion is detected, and has no effect in this case.
172    /// Recursion occurs if 'assign' is called from within the closure supplied to
173    /// the 'ArcShiftCell::get'-method.
174    pub fn assign(&self, other: &ArcShift<T>) -> Result<(), RecursionDetected> {
175        if self.recursion.get() == 0 {
176            // SAFETY:
177            // Getting the inner value is safe, no other thread can be accessing it now
178            *unsafe { &mut *self.inner.get() } = other.clone();
179            Ok(())
180        } else {
181            Err(RecursionDetected)
182        }
183    }
184    /// Reload this ArcShiftCell-instance.
185    /// This allows heap blocks kept alive by this instance of
186    /// ArcShiftCell to be dropped.
187    /// Note, this method only works when not called from within a closure
188    /// supplied to the 'get' method. If such recursion occurs, this method
189    /// does nothing.
190    pub fn reload(&self) {
191        if self.recursion.get() == 0 {
192            // SAFETY:
193            // For 'reload' to be safe, we must be sure that no other execution has a reference
194            // to 'self.item', since the value it points to might be dropped.
195            // This guarantee is fulfilled, because there is no way to get such a reference
196            // other than receiving it in the callback given to 'get'. And for as long as the
197            // callback is executing, 'recursion' will be != 0.
198            unsafe { &mut *self.inner.get() }.reload()
199        }
200    }
201    /// Create an ArcShift-instance pointing to the same value as this cell.
202    pub fn make_arcshift(&self) -> ArcShift<T> {
203        // SAFETY:
204        // ArcShiftCell is not Sync, and 'make_arcshift' does not recursively call into user
205        // code, so we know no other operation can be ongoing.
206        unsafe { &mut *self.inner.get() }.clone()
207    }
208}
209impl<T: ?Sized + 'static> Debug for ArcShiftCell<T>
210where
211    T: Debug,
212{
213    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
214        write!(f, "ArcShiftCell({:?})", &*self.borrow())
215    }
216}