Messages in this thread | | | From | Andreas Hindborg <> | Subject | Re: [PATCH v3 08/13] rust: init: add `stack_pin_init!` macro | Date | Fri, 31 Mar 2023 14:43:33 +0200 |
| |
y86-dev@protonmail.com writes:
> From: Benno Lossin <y86-dev@protonmail.com> > > The `stack_pin_init!` macro allows pin-initializing a value on the > stack. It accepts a `impl PinInit<T, E>` to initialize a `T`. It allows > propagating any errors via `?` or handling it normally via `match`. > > Signed-off-by: Benno Lossin <y86-dev@protonmail.com> > ---
Reviewed-by: Andreas Hindborg <a.hindborg@samsung.com>
> rust/kernel/init.rs | 70 +++++++++++++++++++++++++++++++--- > rust/kernel/init/__internal.rs | 46 ++++++++++++++++++++++ > 2 files changed, 110 insertions(+), 6 deletions(-) > > diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs > index 428b5c2ac516..3358f14beffb 100644 > --- a/rust/kernel/init.rs > +++ b/rust/kernel/init.rs > @@ -12,7 +12,8 @@ > //! > //! To initialize a `struct` with an in-place constructor you will need two things: > //! - an in-place constructor, > -//! - a memory location that can hold your `struct`. > +//! - a memory location that can hold your `struct` (this can be the [stack], an [`Arc<T>`], > +//! [`UniqueArc<T>`], [`Box<T>`] or any other smart pointer that implements [`InPlaceInit`]). > //! > //! To get an in-place constructor there are generally three options: > //! - directly creating an in-place constructor using the [`pin_init!`] macro, > @@ -180,6 +181,7 @@ > //! [pinning]: https://doc.rust-lang.org/std/pin/index.html > //! [structurally pinned fields]: > //! https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field > +//! [stack]: crate::stack_pin_init > //! [`Arc<T>`]: crate::sync::Arc > //! [`impl PinInit<Foo>`]: PinInit > //! [`impl PinInit<T, E>`]: PinInit > @@ -199,6 +201,62 @@ pub mod __internal; > #[doc(hidden)] > pub mod macros; > > +/// Initialize and pin a type directly on the stack. > +/// > +/// # Examples > +/// > +/// ```rust > +/// # #![allow(clippy::disallowed_names, clippy::new_ret_no_self)] > +/// # use kernel::{init, pin_init, stack_pin_init, init::*, sync::Mutex, new_mutex}; > +/// # use macros::pin_data; > +/// # use core::pin::Pin; > +/// #[pin_data] > +/// struct Foo { > +/// #[pin] > +/// a: Mutex<usize>, > +/// b: Bar, > +/// } > +/// > +/// #[pin_data] > +/// struct Bar { > +/// x: u32, > +/// } > +/// > +/// let a = new_mutex!(42, "Foo::a"); > +/// > +/// stack_pin_init!(let foo =? pin_init!(Foo { > +/// a, > +/// b: Bar { > +/// x: 64, > +/// }, > +/// })); > +/// let foo: Pin<&mut Foo> = foo; > +/// # Ok::<(), core::convert::Infallible>(()) > +/// ``` > +/// > +/// # Syntax > +/// > +/// A normal `let` binding with optional type annotation. The expression is expected to implement > +/// [`PinInit`]. Additionally a `?` can be put after the `=`, this will assign `Pin<&mut T>` to the > +/// variable instead of `Result<Pin<&mut T>, E>`. > +#[macro_export] > +macro_rules! stack_pin_init { > + (let $var:ident $(: $t:ty)? = $val:expr) => { > + let mut $var = $crate::init::__internal::StackInit$(::<$t>)?::uninit(); > + let mut $var = { > + let val = $val; > + unsafe { $crate::init::__internal::StackInit::init(&mut $var, val) } > + }; > + }; > + (let $var:ident $(: $t:ty)? =? $val:expr) => { > + let mut $var = $crate::init::__internal::StackInit$(::<$t>)?::uninit(); > + let mut $var = { > + let val = $val; > + unsafe { $crate::init::__internal::StackInit::init(&mut $var, val)? } > + }; > + }; > +} > + > /// Construct an in-place, pinned initializer for `struct`s. > /// > /// This macro defaults the error to [`Infallible`]. If you need [`Error`], then use > @@ -916,8 +974,8 @@ macro_rules! try_init { > /// A pinned initializer for `T`. > /// > /// To use this initializer, you will need a suitable memory location that can hold a `T`. This can > -/// be [`Box<T>`], [`Arc<T>`], [`UniqueArc<T>`]. Use the [`InPlaceInit::pin_init`] function of a > -/// smart pointer like [`Arc<T>`] on this. > +/// be [`Box<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use the > +/// [`InPlaceInit::pin_init`] function of a smart pointer like [`Arc<T>`] on this. > /// > /// Also see the [module description](self). > /// > @@ -952,9 +1010,9 @@ pub unsafe trait PinInit<T: ?Sized, E = Infallible>: Sized { > /// An initializer for `T`. > /// > /// To use this initializer, you will need a suitable memory location that can hold a `T`. This can > -/// be [`Box<T>`], [`Arc<T>`], [`UniqueArc<T>`]. Use the [`InPlaceInit::init`] function of a smart > -/// pointer like [`Arc<T>`] on this. Because [`PinInit<T, E>`] is a super trait, you can > -/// use every function that takes it as well. > +/// be [`Box<T>`], [`Arc<T>`], [`UniqueArc<T>`] or even the stack (see [`stack_pin_init!`]). Use the > +/// [`InPlaceInit::init`] function of a smart pointer like [`Arc<T>`] on this. Because > +/// [`PinInit<T, E>`] is a super trait, you can use every function that takes it as well. > /// > /// Also see the [module description](self). > /// > diff --git a/rust/kernel/init/__internal.rs b/rust/kernel/init/__internal.rs > index 4a3c7bf27a06..bf33c8e96e6d 100644 > --- a/rust/kernel/init/__internal.rs > +++ b/rust/kernel/init/__internal.rs > @@ -89,6 +89,52 @@ unsafe impl<T: ?Sized> HasInitData for T { > } > } > > +/// Stack initializer helper type. Use [`stack_pin_init`] instead of this primitive. > +/// > +/// # Invariants > +/// > +/// If `self.1` is true, then `self.0` is initialized. > +/// > +/// [`stack_pin_init`]: kernel::stack_pin_init > +pub struct StackInit<T>(MaybeUninit<T>, bool); > + > +impl<T> Drop for StackInit<T> { > + #[inline] > + fn drop(&mut self) { > + if self.1 { > + // SAFETY: As we are being dropped, we only call this once. And since `self.1 == true`, > + // `self.0` has to be initialized. > + unsafe { self.0.assume_init_drop() }; > + } > + } > +} > + > +impl<T> StackInit<T> { > + /// Creates a new [`StackInit<T>`] that is uninitialized. Use [`stack_pin_init`] instead of this > + /// primitive. > + /// > + /// [`stack_pin_init`]: kernel::stack_pin_init > + #[inline] > + pub fn uninit() -> Self { > + Self(MaybeUninit::uninit(), false) > + } > + > + /// Initializes the contents and returns the result. > + /// > + /// # Safety > + /// > + /// The caller ensures that `self` is on the stack and not accessible in any other way, if this > + /// function returns `Ok`. > + #[inline] > + pub unsafe fn init<E>(&mut self, init: impl PinInit<T, E>) -> Result<Pin<&mut T>, E> { > + // SAFETY: The memory slot is valid and this type ensures that it will stay pinned. > + unsafe { init.__pinned_init(self.0.as_mut_ptr())? }; > + self.1 = true; > + // SAFETY: The slot is now pinned, since we will never give access to `&mut T`. > + Ok(unsafe { Pin::new_unchecked(self.0.assume_init_mut()) }) > + } > +} > + > /// When a value of this type is dropped, it drops a `T`. > /// > /// Can be forgotton to prevent the drop.
| |