|
15 | 15 | //!
|
16 | 16 | //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
|
17 | 17 |
|
18 |
| -use crate::{bindings, Result}; |
19 |
| -use alloc::boxed::Box; |
| 18 | +use crate::{bindings, Error, Result}; |
| 19 | +use alloc::alloc::{alloc, dealloc}; |
20 | 20 | use core::{
|
21 | 21 | alloc::Layout,
|
22 | 22 | cell::UnsafeCell,
|
@@ -79,23 +79,32 @@ impl<T> Ref<T> {
|
79 | 79 | ///
|
80 | 80 | /// This is useful because it provides a mutable reference to `T` at its final location.
|
81 | 81 | pub fn try_new_and_init<U: FnOnce(Pin<&mut T>)>(contents: T, init: U) -> Result<Self> {
|
| 82 | + let layout = Layout::new::<RefInner<T>>(); |
| 83 | + // SAFETY: The layout size is guaranteed to be non-zero because `RefInner` contains the |
| 84 | + // reference count. |
| 85 | + let mut inner = NonNull::new(unsafe { alloc(layout) }) |
| 86 | + .ok_or(Error::ENOMEM)? |
| 87 | + .cast::<RefInner<T>>(); |
| 88 | + |
82 | 89 | // INVARIANT: The refcount is initialised to a non-zero value.
|
83 |
| - let mut inner = Box::try_new(RefInner { |
| 90 | + let value = RefInner { |
84 | 91 | // SAFETY: Just an FFI call that returns a `refcount_t` initialised to 1.
|
85 | 92 | refcount: UnsafeCell::new(unsafe { bindings::REFCOUNT_INIT(1) }),
|
86 | 93 | data: contents,
|
87 |
| - })?; |
| 94 | + }; |
| 95 | + // SAFETY: `inner` is writable and properly aligned. |
| 96 | + unsafe { inner.as_ptr().write(value) }; |
88 | 97 |
|
89 | 98 | // SAFETY: By the invariant, `RefInner` is pinned and `T` is also pinned.
|
90 |
| - let pinned = unsafe { Pin::new_unchecked(&mut inner.data) }; |
| 99 | + let pinned = unsafe { Pin::new_unchecked(&mut inner.as_mut().data) }; |
91 | 100 |
|
92 | 101 | // INVARIANT: The only places where `&mut T` is available are here, which is explicitly
|
93 | 102 | // pinned, and in `drop`. Both are compatible with the pin requirements.
|
94 | 103 | init(pinned);
|
95 | 104 |
|
96 |
| - // SAFETY: We just created `inner` with a reference count of 1 and we're leaking it. So the |
97 |
| - // new `Ref` object owns the reference. |
98 |
| - Ok(unsafe { Self::from_inner(NonNull::from(Box::leak(inner))) }) |
| 105 | + // SAFETY: We just created `inner` with a reference count of 1, which is owned by the new |
| 106 | + // `Ref` object. |
| 107 | + Ok(unsafe { Self::from_inner(inner) }) |
99 | 108 | }
|
100 | 109 |
|
101 | 110 | /// Deconstructs a [`Ref`] object into a `usize`.
|
@@ -243,9 +252,15 @@ impl<T: ?Sized> Drop for Ref<T> {
|
243 | 252 | let is_zero = unsafe { bindings::refcount_dec_and_test(refcount) };
|
244 | 253 | if is_zero {
|
245 | 254 | // The count reached zero, we must free the memory.
|
246 |
| - // |
247 |
| - // SAFETY: The pointer was initialised from the result of `Box::leak`. |
248 |
| - unsafe { Box::from_raw(self.ptr.as_ptr()) }; |
| 255 | + |
| 256 | + // SAFETY: This thread holds the only remaining reference to `self`, so it is safe to |
| 257 | + // get a mutable reference to it. |
| 258 | + let inner = unsafe { self.ptr.as_mut() }; |
| 259 | + let layout = Layout::for_value(inner); |
| 260 | + // SAFETY: The value stored in inner is valid. |
| 261 | + unsafe { core::ptr::drop_in_place(inner) }; |
| 262 | + // SAFETY: The pointer was initialised from the result of a call to `alloc`. |
| 263 | + unsafe { dealloc(self.ptr.cast().as_ptr(), layout) }; |
249 | 264 | }
|
250 | 265 | }
|
251 | 266 | }
|
|
0 commit comments