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}