|
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,
|
@@ -85,23 +85,32 @@ impl<T> Ref<T> {
|
85 | 85 | ///
|
86 | 86 | /// This is useful because it provides a mutable reference to `T` at its final location.
|
87 | 87 | pub fn try_new_and_init<U: FnOnce(Pin<&mut T>)>(contents: T, init: U) -> Result<Self> {
|
| 88 | + let layout = Layout::new::<RefInner<T>>(); |
| 89 | + // SAFETY: The layout size is guaranteed to be non-zero because `RefInner` contains the |
| 90 | + // reference count. |
| 91 | + let mut inner = NonNull::new(unsafe { alloc(layout) }) |
| 92 | + .ok_or(Error::ENOMEM)? |
| 93 | + .cast::<RefInner<T>>(); |
| 94 | + |
88 | 95 | // INVARIANT: The refcount is initialised to a non-zero value.
|
89 |
| - let mut inner = Box::try_new(RefInner { |
| 96 | + let value = RefInner { |
90 | 97 | // SAFETY: Just an FFI call that returns a `refcount_t` initialised to 1.
|
91 | 98 | refcount: UnsafeCell::new(unsafe { rust_helper_refcount_new() }),
|
92 | 99 | data: contents,
|
93 |
| - })?; |
| 100 | + }; |
| 101 | + // SAFETY: `inner` is writable and properly aligned. |
| 102 | + unsafe { inner.as_ptr().write(value) }; |
94 | 103 |
|
95 | 104 | // SAFETY: By the invariant, `RefInner` is pinned and `T` is also pinned.
|
96 |
| - let pinned = unsafe { Pin::new_unchecked(&mut inner.data) }; |
| 105 | + let pinned = unsafe { Pin::new_unchecked(&mut inner.as_mut().data) }; |
97 | 106 |
|
98 | 107 | // INVARIANT: The only places where `&mut T` is available are here, which is explicitly
|
99 | 108 | // pinned, and in `drop`. Both are compatible with the pin requirements.
|
100 | 109 | init(pinned);
|
101 | 110 |
|
102 |
| - // SAFETY: We just created `inner` with a reference count of 1 and we're leaking it. So the |
103 |
| - // new `Ref` object owns the reference. |
104 |
| - Ok(unsafe { Self::from_inner(NonNull::from(Box::leak(inner))) }) |
| 111 | + // SAFETY: We just created `inner` with a reference count of 1, which is owned by the new |
| 112 | + // `Ref` object. |
| 113 | + Ok(unsafe { Self::from_inner(inner) }) |
105 | 114 | }
|
106 | 115 |
|
107 | 116 | /// Deconstructs a [`Ref`] object into a `usize`.
|
@@ -249,9 +258,15 @@ impl<T: ?Sized> Drop for Ref<T> {
|
249 | 258 | let is_zero = unsafe { rust_helper_refcount_dec_and_test(refcount) };
|
250 | 259 | if is_zero {
|
251 | 260 | // The count reached zero, we must free the memory.
|
252 |
| - // |
253 |
| - // SAFETY: The pointer was initialised from the result of `Box::leak`. |
254 |
| - unsafe { Box::from_raw(self.ptr.as_ptr()) }; |
| 261 | + |
| 262 | + // SAFETY: This thread holds the only remaining reference to `self`, so it is safe to |
| 263 | + // get a mutable reference to it. |
| 264 | + let inner = unsafe { self.ptr.as_mut() }; |
| 265 | + let layout = Layout::for_value(inner); |
| 266 | + // SAFETY: The value stored in inner is valid. |
| 267 | + unsafe { core::ptr::drop_in_place(inner) }; |
| 268 | + // SAFETY: The pointer was initialised from the result of a call to `alloc`. |
| 269 | + unsafe { dealloc(self.ptr.cast().as_ptr(), layout) }; |
255 | 270 | }
|
256 | 271 | }
|
257 | 272 | }
|
|
0 commit comments