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}