Skip to content

Commit 6c4400f

Browse files
committed
rust: switch away from Box in Ref implementation.
This is needed because we will introduce `TryFrom<Vec<T>>` for `Ref<[T]>`, which cannot be implemented with `Box`. For the drop implementation to be compatible with both constructors, we need them to use the same allocator. This is in preparation for deprecating `Arc<T>`. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 2b10d1b commit 6c4400f

File tree

1 file changed

+26
-11
lines changed

1 file changed

+26
-11
lines changed

rust/kernel/sync/arc.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
//!
1616
//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
1717
18-
use crate::{bindings, Result};
19-
use alloc::boxed::Box;
18+
use crate::{bindings, Error, Result};
19+
use alloc::alloc::{alloc, dealloc};
2020
use core::{
2121
alloc::Layout,
2222
cell::UnsafeCell,
@@ -85,23 +85,32 @@ impl<T> Ref<T> {
8585
///
8686
/// This is useful because it provides a mutable reference to `T` at its final location.
8787
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+
8895
// INVARIANT: The refcount is initialised to a non-zero value.
89-
let mut inner = Box::try_new(RefInner {
96+
let value = RefInner {
9097
// SAFETY: Just an FFI call that returns a `refcount_t` initialised to 1.
9198
refcount: UnsafeCell::new(unsafe { rust_helper_refcount_new() }),
9299
data: contents,
93-
})?;
100+
};
101+
// SAFETY: `inner` is writable and properly aligned.
102+
unsafe { inner.as_ptr().write(value) };
94103

95104
// 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) };
97106

98107
// INVARIANT: The only places where `&mut T` is available are here, which is explicitly
99108
// pinned, and in `drop`. Both are compatible with the pin requirements.
100109
init(pinned);
101110

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) })
105114
}
106115

107116
/// Deconstructs a [`Ref`] object into a `usize`.
@@ -249,9 +258,15 @@ impl<T: ?Sized> Drop for Ref<T> {
249258
let is_zero = unsafe { rust_helper_refcount_dec_and_test(refcount) };
250259
if is_zero {
251260
// 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) };
255270
}
256271
}
257272
}

0 commit comments

Comments
 (0)