refbox/internals.rs
1//! Contains the internal state machine and heap part of a `RefBox`.
2
3use crate::RefBox;
4use core::cell::{Cell, UnsafeCell};
5use core::marker::PhantomData;
6use core::ptr::{self, NonNull};
7use std::alloc;
8
9#[cfg(feature = "cyclic_stable")]
10use std::alloc::Layout;
11
12///////////////////////////////////////////////////////////////////////////////
13// Heap Part Types
14///////////////////////////////////////////////////////////////////////////////
15
16/// The reference counter.
17// Note: when changing this also change the public documentation.
18pub(crate) type WeakCount = u32;
19
20/// The current status of a `RefBox`.
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub(crate) enum Status {
23 Available,
24 Borrowed,
25 Dropped,
26 DroppedWhileBorrowed,
27}
28
29/// The status part of the RefBoxHeap.
30#[derive(Debug)]
31pub(crate) struct RefBoxHeapInner {
32 /// The current status of the data.
33 status: Cell<Status>,
34 /// The number of weak references to the data.
35 weak_count: Cell<WeakCount>,
36 /// The layout of the `RefBoxHeap<T>`.
37 ///
38 /// Necessary to deallocate the memory of the heap part when feature `cyclic_stable` is enabled.
39 #[cfg(feature = "cyclic_stable")]
40 layout: Layout,
41}
42
43/// The part of a `RefBox` that is stored on the heap.
44// repr(C) because we are casting in `new_cyclic`.
45#[repr(C)]
46#[derive(Debug)]
47pub struct RefBoxHeap<T: ?Sized> {
48 pub(crate) inner: RefBoxHeapInner,
49 pub(crate) data: UnsafeCell<T>,
50}
51
52impl RefBoxHeapInner {
53 /// Returns the current status.
54 #[inline]
55 pub(crate) fn status(&self) -> Status {
56 self.status.get()
57 }
58
59 /// Returns the current weak reference count.
60 #[inline]
61 pub(crate) fn weak_count(&self) -> WeakCount {
62 self.weak_count.get()
63 }
64
65 /// The layout of the entire `RefBoxHeap<T>`.
66 ///
67 /// Necessary to deallocate the memory of the heap part when feature `cyclic_stable` is enabled.
68 #[cfg(feature = "cyclic_stable")]
69 #[inline]
70 fn layout(&self) -> Layout {
71 self.layout
72 }
73
74 /// Sets the weak reference count. Used in tests.
75 #[inline]
76 #[cfg(test)]
77 pub(crate) fn set_weak_count(&self, count: WeakCount) {
78 self.weak_count.set(count);
79 }
80
81 /// Increases the reference counter by 1.
82 ///
83 /// # Panics
84 ///
85 /// Panics if the number of `Weak`s overflows `RefCount::MAX`.
86 #[inline]
87 pub(crate) fn increase_weak_count(&self) {
88 let refcount = self.weak_count();
89
90 if refcount == WeakCount::MAX {
91 cold_panic();
92 } else {
93 self.weak_count.set(refcount + 1);
94 }
95 }
96
97 /// Increases the reference counter by 1.
98 ///
99 /// Returns false if the refcount overflowed.
100 #[inline]
101 pub(crate) fn try_increase_weak_count(&self) -> bool {
102 let refcount = self.weak_count();
103
104 if refcount == WeakCount::MAX {
105 cold_false()
106 } else {
107 self.weak_count.set(refcount + 1);
108 true
109 }
110 }
111
112 /// Decreases the reference counter by 1.
113 #[inline]
114 fn decrease_refcount(&self) -> WeakCount {
115 let refcount = self.weak_count() - 1;
116 self.weak_count.set(refcount);
117 refcount
118 }
119
120 /// Returns true if the owner is alive.
121 #[inline]
122 pub(crate) fn is_alive(&self) -> bool {
123 matches!(self.status(), Status::Available | Status::Borrowed)
124 }
125
126 /// Returns true if the data is currently borrowed.
127 #[inline]
128 pub(crate) fn is_borrowed(&self) -> bool {
129 matches!(self.status(), Status::Borrowed)
130 }
131
132 /// Sets the status to `Borrowed`.
133 #[inline]
134 pub(crate) fn start_borrow(&self) {
135 self.status.set(Status::Borrowed);
136 }
137}
138
139impl<T: ?Sized> RefBoxHeap<T> {
140 /// Returns a shared reference to the data.
141 ///
142 /// # Safety
143 ///
144 /// 1. Ensure there are no mutable references to `T`.
145 /// 2. Ensure `T` is initialized.
146 /// 3. Ensure `T` is not dropped.
147 #[inline]
148 pub(crate) unsafe fn data_ref(&self) -> &T {
149 // SAFETY: the caller must uphold the safety requirements
150 unsafe { &*self.data.get() }
151 }
152
153 /// Returns a unique reference to the data.
154 ///
155 /// # Safety
156 ///
157 /// 1. Ensure there are no other references to `T`.
158 /// 2. Ensure `T` is initialized.
159 /// 3. Ensure `T` is not dropped.
160 #[inline]
161 pub(crate) unsafe fn data_mut(&self) -> &mut T {
162 // SAFETY: this goes through UnsafeCell, and its documentation
163 // states it is allowed to have a shared reference to the cell and
164 // a mutable reference to the content of the cell simultaneously, as
165 // long as there are no other references to the content of the cell.
166 unsafe { &mut *self.data.get() }
167 }
168
169 /// Runs the destructor of the data.
170 ///
171 /// # Safety
172 ///
173 /// 1. Ensure there are no references to `T`.
174 /// 2. Ensure `T` is initialized.
175 /// 3. Ensure `T` is not already dropped.
176 pub(crate) unsafe fn drop_data(&self) {
177 // SAFETY: the caller must uphold the safety requirements
178 unsafe { ptr::drop_in_place(self.data.get()) };
179 self.inner.status.set(Status::Dropped);
180 }
181}
182
183/// Panics.
184///
185/// Is unlikely to be called, so it has a 'cold' attribute for optimization.
186#[cold]
187#[inline(never)]
188fn cold_panic() {
189 panic!()
190}
191
192/// Returns false.
193///
194/// Is unlikely to be called, so it has a 'cold' attribute for optimization.
195#[cold]
196#[inline(never)]
197fn cold_false() -> bool {
198 false
199}
200
201///////////////////////////////////////////////////////////////////////////////
202// Constructors
203///////////////////////////////////////////////////////////////////////////////
204
205/// Creates a new pointer.
206#[inline]
207pub(crate) fn new_ref_box<T>(value: T) -> RefBox<T> {
208 let heap = Box::into_raw(Box::new(RefBoxHeap {
209 inner: RefBoxHeapInner {
210 status: Cell::new(Status::Available),
211 weak_count: Cell::new(0),
212 #[cfg(feature = "cyclic_stable")]
213 layout: Layout::new::<RefBoxHeap<T>>(),
214 },
215 data: UnsafeCell::new(value),
216 }));
217
218 // SAFETY: `Box::into_raw` ensures the pointer is non-null.
219 let ptr = unsafe { NonNull::new_unchecked(heap) };
220
221 RefBox {
222 ptr,
223 _p: PhantomData,
224 }
225}
226
227/// Creates a new `RefBox` through a closure which receives a
228/// `RefBoxRef` to the same data.
229#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
230#[inline]
231pub(crate) fn new_cyclic_ref_box<T, F: FnOnce(&crate::Weak<T>) -> T>(op: F) -> RefBox<T> {
232 // Allocate the heap data with uninitialized T data.
233 // SAFETY: `status` is set to `Dropped` to avoid being able to access
234 // the uninitialized data in the closure.
235 let heap = Box::into_raw(Box::new(RefBoxHeap {
236 inner: RefBoxHeapInner {
237 status: Cell::new(Status::Dropped),
238 weak_count: Cell::new(1),
239 #[cfg(feature = "cyclic_stable")]
240 layout: Layout::new::<RefBoxHeap<T>>(),
241 },
242 data: UnsafeCell::new(core::mem::MaybeUninit::<T>::uninit()),
243 }));
244
245 // SAFETY: `Box::into_raw` ensures the pointer is non-null.
246 let weak = crate::Weak {
247 ptr: unsafe { NonNull::new_unchecked(heap.cast()) },
248 };
249
250 // We get the real instance by executing the closure.
251 // SAFETY (1): The weak reference is passed by reference to make sure the
252 // memory is not deallocated at the end of the closure.
253 // SAFETY (2): If this panics, `Weak` will deallocate the heap
254 // memory, but since the status was set to `Dropped`, it will not run
255 // drop on the uninitialized memory.
256 let data = op(&weak);
257
258 // We write the data without reading the old one.
259 // SAFETY (1): there are no other mutable references to the data field. The RefBox cannot
260 // be borrowed because the status is `Dropped`.
261 // SAFETY (2): the references cover uninitialized data, but it is behind MaybeUninit
262 // so they are allowed here.
263 unsafe { (*heap).data.get_mut().write(data) };
264
265 // SAFETY: by now the bytes in the data field are initialized.
266 unsafe { (*heap).inner.status.set(Status::Available) };
267
268 // The weak pointer is explicitly dropped here to ensure it is not dropped sooner.
269 drop(weak);
270
271 RefBox {
272 ptr: unsafe { NonNull::new_unchecked(heap.cast()) },
273 _p: PhantomData,
274 }
275}
276
277///////////////////////////////////////////////////////////////////////////////
278// Dropping & Deallocating
279///////////////////////////////////////////////////////////////////////////////
280
281/// Deallocates the heap part of the `RefBox`.
282unsafe fn dealloc_heap<T: ?Sized>(heap: NonNull<RefBoxHeap<T>>) {
283 // When both `cyclic` and `cyclic_stable` are enabled, this version
284 // is preferred to be extra sure about the safety.
285 #[cfg(feature = "cyclic_stable")]
286 let layout = unsafe { &(*heap.as_ptr()).inner }.layout();
287
288 // In case of a panic in new_cyclic, `heap` contains partially
289 // uninitialized memory. It is UB to have a reference to uninitialized
290 // memory, so we need to get the layout through a raw pointer. This
291 // requires Nightly feature 'layout_for_ptr'.
292 #[cfg(all(feature = "cyclic", not(feature = "cyclic_stable")))]
293 let layout = unsafe { alloc::Layout::for_value_raw(heap.as_ptr()) };
294
295 #[cfg(all(not(feature = "cyclic"), not(feature = "cyclic_stable")))]
296 let layout = alloc::Layout::for_value(unsafe { heap.as_ref() });
297
298 unsafe {
299 alloc::dealloc(heap.as_ptr().cast(), layout);
300 }
301}
302
303/// Called when the owning RefBox is dropped.
304#[inline]
305pub(crate) unsafe fn drop_ref_box<T: ?Sized>(heap: NonNull<RefBoxHeap<T>>) {
306 // SAFETY: the data of the owner is always initialized,
307 // so we can create references to the RefBoxHeap.
308
309 match unsafe { heap.as_ref() }.inner.status() {
310 Status::Available => {
311 // If there is no active borrow, the data should
312 // be dropped when the owner is dropped.
313 // SAFETY: the status is `Available`, so the `RefBoxHeap` is initialized, there are no
314 // other references to it, and it is not yet dropped.
315 unsafe { heap.as_ref().drop_data() };
316
317 // If there are no weak references, the heap
318 // part should be deallocated as well.
319 if unsafe { heap.as_ref() }.inner.weak_count() == 0 {
320 // SAFETY: there are no more references to the data.
321 unsafe { dealloc_heap(heap) };
322 }
323 }
324 Status::Borrowed => {
325 // It is possible to drop the owner of the data while
326 // a borrow is active. In that case, dropping of the data is
327 // delayed until the borrow is dropped.
328 unsafe { heap.as_ref() }
329 .inner
330 .status
331 .set(Status::DroppedWhileBorrowed);
332 }
333 Status::DroppedWhileBorrowed | Status::Dropped => {
334 // SAFETY: if the status is `DroppedWhileBorrowed` or `Dropped` it means
335 // the RefBox is dropped a second time which is already UB.
336 unsafe { std::hint::unreachable_unchecked() };
337 }
338 }
339}
340
341/// Called when a weak pointer is dropped.
342#[inline]
343pub(crate) unsafe fn drop_weak<T: ?Sized>(heap: NonNull<RefBoxHeap<T>>) {
344 // SAFETY: the data of a Weak pointer may be uninitialized, so we have to
345 // make sure not to create a reference that covers the `data` field
346 // of RefBoxHeap.
347
348 // Decrease the reference count.
349 let refcount = unsafe { &(*heap.as_ptr()).inner }.decrease_refcount();
350
351 // If there are no more references and the owner is dropped,
352 // the data needs to be deallocated.
353 if refcount == 0 {
354 if unsafe { &(*heap.as_ptr()).inner }.status() == Status::Dropped {
355 // SAFETY: there are no more references to the heap part.
356 unsafe { dealloc_heap(heap) };
357 }
358 }
359}
360
361/// Called when a Borrow is dropped.
362#[inline]
363pub(crate) unsafe fn drop_borrow<T: ?Sized>(heap: &RefBoxHeap<T>) {
364 match heap.inner.status() {
365 Status::Borrowed => {
366 heap.inner.status.set(Status::Available);
367 }
368 Status::DroppedWhileBorrowed => {
369 // The owner was dropped during the lifetime of the borrow.
370 // Dropping is delayed till the end of the borrow.
371 // SAFETY: after this the data cannot be accessed anymore,
372 // because drop_data sets the status to `Dropped`.
373 unsafe { heap.drop_data() };
374 }
375 Status::Available | Status::Dropped => {
376 // SAFETY: It is not possible to create a borrow if the status is `Dropped`,
377 // and it is not possible for the status to become `Dropped` while a
378 // `Borrow` exists, because if the owner is dropped during an active
379 // borrow, the status is set to `DroppedWhileBorrowed`.
380 // It is also not possible for the status to be `Available`, as it is
381 // set to `Borrowed` when the `Borrow` is created.
382 unsafe { std::hint::unreachable_unchecked() };
383 }
384 }
385}