All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wedson Almeida Filho <wedsonaf@gmail.com>
To: rust-for-linux@vger.kernel.org
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	linux-kernel@vger.kernel.org,
	"Wedson Almeida Filho" <wedsonaf@gmail.com>
Subject: [PATCH 6/7] rust: sync: introduce `UniqueArc`
Date: Wed, 28 Dec 2022 06:03:45 +0000	[thread overview]
Message-ID: <20221228060346.352362-6-wedsonaf@gmail.com> (raw)
In-Reply-To: <20221228060346.352362-1-wedsonaf@gmail.com>

Since `Arc<T>` 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<T>` addresses this problem essentially being an `Arc<T>` that
has a refcount of 1 and is therefore writable. Once initialisation is
completed, it can be transitioned (without failure paths) into an
`Arc<T>`.

Suggested-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
---
 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<T: ?Sized> Drop for Arc<T> {
     }
 }
 
+impl<T: ?Sized> From<UniqueArc<T>> for Arc<T> {
+    fn from(item: UniqueArc<T>) -> Self {
+        item.inner
+    }
+}
+
+impl<T: ?Sized> From<Pin<UniqueArc<T>>> for Arc<T> {
+    fn from(item: Pin<UniqueArc<T>>) -> 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<T: ?Sized> 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<Test>` 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<Arc<Example>> {
+///     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<Example>`. 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<Arc<Example>> {
+///     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<Example>`; 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<Arc<Example>> {
+///     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<T: ?Sized> {
+    inner: Arc<T>,
+}
+
+impl<T> UniqueArc<T> {
+    /// Tries to allocate a new [`UniqueArc`] instance.
+    pub fn try_new(value: T) -> Result<Self> {
+        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<UniqueArc<MaybeUninit<T>>> {
+        Ok(UniqueArc::<MaybeUninit<T>> {
+            // INVARIANT: The newly-created object has a ref-count of 1.
+            inner: Arc::try_new(MaybeUninit::uninit())?,
+        })
+    }
+}
+
+impl<T> UniqueArc<MaybeUninit<T>> {
+    /// Converts a `UniqueArc<MaybeUninit<T>>` into a `UniqueArc<T>` by writing a value into it.
+    pub fn write(mut self, value: T) -> UniqueArc<T> {
+        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<T>` is compatible with `T`.
+            inner: unsafe { Arc::from_inner(inner.cast()) },
+        }
+    }
+}
+
+impl<T: ?Sized> From<UniqueArc<T>> for Pin<UniqueArc<T>> {
+    fn from(obj: UniqueArc<T>) -> Self {
+        // SAFETY: It is not possible to move/replace `T` inside a `Pin<UniqueArc<T>>` (unless `T`
+        // is `Unpin`), so it is ok to convert it to `Pin<UniqueArc<T>>`.
+        unsafe { Pin::new_unchecked(obj) }
+    }
+}
+
+impl<T: ?Sized> Deref for UniqueArc<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        self.inner.deref()
+    }
+}
+
+impl<T: ?Sized> DerefMut for UniqueArc<T> {
+    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 }
+    }
+}
-- 
2.34.1


  parent reply	other threads:[~2022-12-28  6:04 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-28  6:03 [PATCH 1/7] rust: sync: add `Arc` for ref-counted allocations Wedson Almeida Filho
2022-12-28  6:03 ` [PATCH 2/7] rust: sync: allow type of `self` to be `Arc<T>` or variants Wedson Almeida Filho
2022-12-28 10:03   ` Alice Ryhl
2022-12-31 19:37   ` Gary Guo
2023-01-04 16:12   ` Vincenzo
2022-12-28  6:03 ` [PATCH 3/7] rust: sync: allow coercion from `Arc<T>` to `Arc<U>` Wedson Almeida Filho
2022-12-28 10:03   ` Alice Ryhl
2022-12-31 19:37   ` Gary Guo
2023-01-04 16:13   ` Vincenzo
2022-12-28  6:03 ` [PATCH 4/7] rust: sync: introduce `ArcBorrow` Wedson Almeida Filho
2022-12-28  7:15   ` Laine Taffin Altman
2022-12-28 10:03   ` Alice Ryhl
2022-12-31 19:43   ` Gary Guo
2023-01-04 15:29     ` Wedson Almeida Filho
2023-01-04 16:06       ` Emilio Cobos Álvarez
2023-01-04 17:52         ` Wedson Almeida Filho
2023-01-16 22:07         ` Gary Guo
2023-01-21  0:44           ` Boqun Feng
2023-01-16 22:42   ` Vincenzo Palazzo
2022-12-28  6:03 ` [PATCH 5/7] rust: sync: allow type of `self` to be `ArcBorrow<T>` Wedson Almeida Filho
2022-12-28 10:04   ` Alice Ryhl
2022-12-31 19:44   ` Gary Guo
2023-01-04 16:18   ` Vincenzo
2022-12-28  6:03 ` Wedson Almeida Filho [this message]
2022-12-28  7:19   ` [PATCH 6/7] rust: sync: introduce `UniqueArc` Laine Taffin Altman
2022-12-31 19:46     ` Gary Guo
2022-12-28 10:04   ` Alice Ryhl
2022-12-31 19:47   ` Gary Guo
2023-01-04 16:21   ` Vincenzo
2022-12-28  6:03 ` [PATCH 7/7] rust: sync: add support for dispatching on Arc and ArcBorrow Wedson Almeida Filho
2022-12-28  7:24   ` Laine Taffin Altman
2022-12-31 19:51     ` Gary Guo
2022-12-28 10:04   ` Alice Ryhl
2022-12-31 19:52   ` Gary Guo
2023-01-04 16:26   ` Vincenzo
2022-12-28  7:09 ` [PATCH 1/7] rust: sync: add `Arc` for ref-counted allocations Laine Taffin Altman
2022-12-28  7:27   ` Wedson Almeida Filho
2022-12-28  7:32     ` Laine Taffin Altman
2022-12-28 10:03 ` Alice Ryhl
2022-12-28 10:14   ` Wedson Almeida Filho
2022-12-28 10:38     ` Alice Ryhl
2022-12-31 19:55 ` Gary Guo
2023-01-04 16:08 ` Vincenzo
2023-01-10 20:22 ` Boqun Feng
2023-01-10 21:20   ` Peter Zijlstra
2023-01-10 21:36     ` Boqun Feng
2023-01-10 21:54       ` Peter Zijlstra
2023-01-16 21:06   ` Boqun Feng
2023-01-16 23:34 ` Miguel Ojeda

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221228060346.352362-6-wedsonaf@gmail.com \
    --to=wedsonaf@gmail.com \
    --cc=alex.gaynor@gmail.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.