Skip to content

Commit 8641502

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 4cb694e commit 8641502

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,
@@ -79,23 +79,32 @@ impl<T> Ref<T> {
7979
///
8080
/// This is useful because it provides a mutable reference to `T` at its final location.
8181
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+
8289
// INVARIANT: The refcount is initialised to a non-zero value.
83-
let mut inner = Box::try_new(RefInner {
90+
let value = RefInner {
8491
// SAFETY: Just an FFI call that returns a `refcount_t` initialised to 1.
8592
refcount: UnsafeCell::new(unsafe { bindings::REFCOUNT_INIT(1) }),
8693
data: contents,
87-
})?;
94+
};
95+
// SAFETY: `inner` is writable and properly aligned.
96+
unsafe { inner.as_ptr().write(value) };
8897

8998
// 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) };
91100

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

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) })
99108
}
100109

101110
/// Deconstructs a [`Ref`] object into a `usize`.
@@ -243,9 +252,15 @@ impl<T: ?Sized> Drop for Ref<T> {
243252
let is_zero = unsafe { bindings::refcount_dec_and_test(refcount) };
244253
if is_zero {
245254
// 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) };
249264
}
250265
}
251266
}

0 commit comments

Comments
 (0)