Skip to content

Commit daa2285

Browse files
committed
rust: sync: Added try_new_uninit to UniqueArc
Also added `assume_init` to `UniqueArc<MaybeUninit<T>>`. It assumes that its contents have been initialized and returns `UniqueArc<T>`. `try_new_uninit` is needed, because `try_new(MaybeUninit::uninit())` actually allocates memory on the stack and may result in a stack overflow if `T` is large in size. `try_new_uninit` has been implemented in such a way that this cannot happen. Signed-off-by: Benno Lossin <[email protected]>
1 parent e50706a commit daa2285

File tree

1 file changed

+37
-2
lines changed

1 file changed

+37
-2
lines changed

rust/kernel/sync/arc.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use core::{
2727
mem::{ManuallyDrop, MaybeUninit},
2828
ops::{Deref, DerefMut},
2929
pin::Pin,
30-
ptr::{self, NonNull},
30+
ptr::{self, addr_of_mut, NonNull},
3131
};
3232

3333
/// A reference-counted pointer to an instance of `T`.
@@ -464,9 +464,33 @@ impl<T> UniqueArc<T> {
464464

465465
/// Tries to allocate a new [`UniqueArc`] instance whose contents are not initialised yet.
466466
pub fn try_new_uninit() -> Result<UniqueArc<MaybeUninit<T>>> {
467+
let layout = Layout::new::<ArcInner<MaybeUninit<T>>>();
468+
// SAFETY: The layout size is guaranteed to be non-zero because `ArcInner` contains the
469+
// reference count.
470+
let inner = NonNull::new(unsafe { alloc(layout) })
471+
.ok_or(ENOMEM)?
472+
.cast::<ArcInner<MaybeUninit<T>>>();
473+
// TODO do this using `pinned-init`
474+
475+
// INVARIANT: The refcount is initialised to a non-zero value.
476+
let refcount = Opaque::new(new_refcount());
477+
// SAFETY: `inner` is writable and properly aligned.
478+
unsafe { addr_of_mut!((*inner.as_ptr()).refcount).write(refcount) };
479+
// assert that there are only two fields: refcount and data (done in a closure to avoid
480+
// overflowing the stack in debug mode with a big `T`)
481+
#[allow(unreachable_code, clippy::diverging_sub_expression)]
482+
let _check = || {
483+
let _check: ArcInner<MaybeUninit<T>> = ArcInner {
484+
refcount: todo!(),
485+
data: todo!(),
486+
};
487+
};
488+
489+
// SAFETY: We just created `inner` with a reference count of 1, which is owned by the new
490+
// `Arc` object.
467491
Ok(UniqueArc::<MaybeUninit<T>> {
468492
// INVARIANT: The newly-created object has a ref-count of 1.
469-
inner: Arc::try_new(MaybeUninit::uninit())?,
493+
inner: unsafe { Arc::from_inner(inner) },
470494
})
471495
}
472496
}
@@ -475,6 +499,17 @@ impl<T> UniqueArc<MaybeUninit<T>> {
475499
/// Converts a `UniqueArc<MaybeUninit<T>>` into a `UniqueArc<T>` by writing a value into it.
476500
pub fn write(mut self, value: T) -> UniqueArc<T> {
477501
self.deref_mut().write(value);
502+
// SAFETY: we just initialized `self`
503+
unsafe { self.assume_init() }
504+
}
505+
506+
/// Unsafely assume that `self` is initialized.
507+
///
508+
/// # Safety
509+
///
510+
/// The caller guarantees that the value behind this pointer has been initialized. It is
511+
/// *immediate* UB to call this when the value is not initialized.
512+
pub unsafe fn assume_init(self) -> UniqueArc<T> {
478513
let inner = ManuallyDrop::new(self).inner.ptr;
479514
UniqueArc {
480515
// SAFETY: The new `Arc` is taking over `ptr` from `self.inner` (which won't be

0 commit comments

Comments
 (0)