From 70e42ebbf6416e8005d8e08ae521b7d5cc5a8b3a Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Wed, 28 Dec 2022 06:03:45 +0000 Subject: [PATCH] rust: sync: introduce `UniqueArc` Since `Arc` does not allow mutating `T` directly (i.e., without inner mutability), it is currently not possible to do some initialisation of `T` post construction but before being shared. `UniqueArc` addresses this problem essentially being an `Arc` that has a refcount of 1 and is therefore writable. Once initialisation is completed, it can be transitioned (without failure paths) into an `Arc`. Suggested-by: Gary Guo Signed-off-by: Wedson Almeida Filho Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Reviewed-by: Vincenzo Palazzo Acked-by: Boqun Feng Signed-off-by: Miguel Ojeda --- rust/kernel/sync.rs | 2 +- rust/kernel/sync/arc.rs | 152 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 3 deletions(-) diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 5de03ea83ea1..33da23e3076d 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -7,4 +7,4 @@ mod arc; -pub use arc::{Arc, ArcBorrow}; +pub use arc::{Arc, ArcBorrow, UniqueArc}; diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 84f31c85a513..832bafc74a90 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -19,8 +19,9 @@ use crate::{bindings, error::Result, types::Opaque}; use alloc::boxed::Box; use core::{ marker::{PhantomData, Unsize}, - mem::ManuallyDrop, - ops::Deref, + mem::{ManuallyDrop, MaybeUninit}, + ops::{Deref, DerefMut}, + pin::Pin, ptr::NonNull, }; @@ -222,6 +223,19 @@ impl Drop for Arc { } } +impl From> for Arc { + fn from(item: UniqueArc) -> Self { + item.inner + } +} + +impl From>> for Arc { + fn from(item: Pin>) -> Self { + // SAFETY: The type invariants of `Arc` guarantee that the data is pinned. + unsafe { Pin::into_inner_unchecked(item).inner } + } +} + /// A borrowed reference to an [`Arc`] instance. /// /// For cases when one doesn't ever need to increment the refcount on the allocation, it is simpler @@ -328,3 +342,137 @@ impl Deref for ArcBorrow<'_, T> { unsafe { &self.inner.as_ref().data } } } + +/// A refcounted object that is known to have a refcount of 1. +/// +/// It is mutable and can be converted to an [`Arc`] so that it can be shared. +/// +/// # Invariants +/// +/// `inner` always has a reference count of 1. +/// +/// # Examples +/// +/// In the following example, we make changes to the inner object before turning it into an +/// `Arc` object (after which point, it cannot be mutated directly). Note that `x.into()` +/// cannot fail. +/// +/// ``` +/// use kernel::sync::{Arc, UniqueArc}; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn test() -> Result> { +/// let mut x = UniqueArc::try_new(Example { a: 10, b: 20 })?; +/// x.a += 1; +/// x.b += 1; +/// Ok(x.into()) +/// } +/// +/// # test().unwrap(); +/// ``` +/// +/// In the following example we first allocate memory for a ref-counted `Example` but we don't +/// initialise it on allocation. We do initialise it later with a call to [`UniqueArc::write`], +/// followed by a conversion to `Arc`. This is particularly useful when allocation happens +/// in one context (e.g., sleepable) and initialisation in another (e.g., atomic): +/// +/// ``` +/// use kernel::sync::{Arc, UniqueArc}; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn test() -> Result> { +/// let x = UniqueArc::try_new_uninit()?; +/// Ok(x.write(Example { a: 10, b: 20 }).into()) +/// } +/// +/// # test().unwrap(); +/// ``` +/// +/// In the last example below, the caller gets a pinned instance of `Example` while converting to +/// `Arc`; this is useful in scenarios where one needs a pinned reference during +/// initialisation, for example, when initialising fields that are wrapped in locks. +/// +/// ``` +/// use kernel::sync::{Arc, UniqueArc}; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn test() -> Result> { +/// let mut pinned = Pin::from(UniqueArc::try_new(Example { a: 10, b: 20 })?); +/// // We can modify `pinned` because it is `Unpin`. +/// pinned.as_mut().a += 1; +/// Ok(pinned.into()) +/// } +/// +/// # test().unwrap(); +/// ``` +pub struct UniqueArc { + inner: Arc, +} + +impl UniqueArc { + /// Tries to allocate a new [`UniqueArc`] instance. + pub fn try_new(value: T) -> Result { + Ok(Self { + // INVARIANT: The newly-created object has a ref-count of 1. + inner: Arc::try_new(value)?, + }) + } + + /// Tries to allocate a new [`UniqueArc`] instance whose contents are not initialised yet. + pub fn try_new_uninit() -> Result>> { + Ok(UniqueArc::> { + // INVARIANT: The newly-created object has a ref-count of 1. + inner: Arc::try_new(MaybeUninit::uninit())?, + }) + } +} + +impl UniqueArc> { + /// Converts a `UniqueArc>` into a `UniqueArc` by writing a value into it. + pub fn write(mut self, value: T) -> UniqueArc { + self.deref_mut().write(value); + let inner = ManuallyDrop::new(self).inner.ptr; + UniqueArc { + // SAFETY: The new `Arc` is taking over `ptr` from `self.inner` (which won't be + // dropped). The types are compatible because `MaybeUninit` is compatible with `T`. + inner: unsafe { Arc::from_inner(inner.cast()) }, + } + } +} + +impl From> for Pin> { + fn from(obj: UniqueArc) -> Self { + // SAFETY: It is not possible to move/replace `T` inside a `Pin>` (unless `T` + // is `Unpin`), so it is ok to convert it to `Pin>`. + unsafe { Pin::new_unchecked(obj) } + } +} + +impl Deref for UniqueArc { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner.deref() + } +} + +impl DerefMut for UniqueArc { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: By the `Arc` type invariant, there is necessarily a reference to the object, so + // it is safe to dereference it. Additionally, we know there is only one reference when + // it's inside a `UniqueArc`, so it is safe to get a mutable reference. + unsafe { &mut self.inner.ptr.as_mut().data } + } +}