mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
rust: init: add stack_pin_init!
macro
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 <benno.lossin@proton.me> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Andreas Hindborg <a.hindborg@samsung.com> Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://lore.kernel.org/r/20230408122429.1103522-11-y86-dev@protonmail.com Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
parent
d0fdc39612
commit
6841d45a30
@ -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
|
||||
@ -202,6 +204,132 @@ 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,
|
||||
/// }
|
||||
///
|
||||
/// stack_pin_init!(let foo = pin_init!(Foo {
|
||||
/// a <- new_mutex!(42),
|
||||
/// b: Bar {
|
||||
/// x: 64,
|
||||
/// },
|
||||
/// }));
|
||||
/// let foo: Pin<&mut Foo> = foo;
|
||||
/// pr_info!("a: {}", &*foo.a.lock());
|
||||
/// ```
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// A normal `let` binding with optional type annotation. The expression is expected to implement
|
||||
/// [`PinInit`]/[`Init`] with the error type [`Infallible`]. If you want to use a different error
|
||||
/// type, then use [`stack_try_pin_init!`].
|
||||
#[macro_export]
|
||||
macro_rules! stack_pin_init {
|
||||
(let $var:ident $(: $t:ty)? = $val:expr) => {
|
||||
let val = $val;
|
||||
let mut $var = ::core::pin::pin!($crate::init::__internal::StackInit$(::<$t>)?::uninit());
|
||||
let mut $var = match $crate::init::__internal::StackInit::init($var, val) {
|
||||
Ok(res) => res,
|
||||
Err(x) => {
|
||||
let x: ::core::convert::Infallible = x;
|
||||
match x {}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// 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_try_pin_init, init::*, sync::Mutex, new_mutex};
|
||||
/// # use macros::pin_data;
|
||||
/// # use core::{alloc::AllocError, pin::Pin};
|
||||
/// #[pin_data]
|
||||
/// struct Foo {
|
||||
/// #[pin]
|
||||
/// a: Mutex<usize>,
|
||||
/// b: Box<Bar>,
|
||||
/// }
|
||||
///
|
||||
/// struct Bar {
|
||||
/// x: u32,
|
||||
/// }
|
||||
///
|
||||
/// stack_try_pin_init!(let foo: Result<Pin<&mut Foo>, AllocError> = pin_init!(Foo {
|
||||
/// a <- new_mutex!(42),
|
||||
/// b: Box::try_new(Bar {
|
||||
/// x: 64,
|
||||
/// })?,
|
||||
/// }));
|
||||
/// let foo = foo.unwrap();
|
||||
/// pr_info!("a: {}", &*foo.a.lock());
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![allow(clippy::disallowed_names, clippy::new_ret_no_self)]
|
||||
/// # use kernel::{init, pin_init, stack_try_pin_init, init::*, sync::Mutex, new_mutex};
|
||||
/// # use macros::pin_data;
|
||||
/// # use core::{alloc::AllocError, pin::Pin};
|
||||
/// #[pin_data]
|
||||
/// struct Foo {
|
||||
/// #[pin]
|
||||
/// a: Mutex<usize>,
|
||||
/// b: Box<Bar>,
|
||||
/// }
|
||||
///
|
||||
/// struct Bar {
|
||||
/// x: u32,
|
||||
/// }
|
||||
///
|
||||
/// stack_try_pin_init!(let foo: Pin<&mut Foo> =? pin_init!(Foo {
|
||||
/// a <- new_mutex!(42),
|
||||
/// b: Box::try_new(Bar {
|
||||
/// x: 64,
|
||||
/// })?,
|
||||
/// }));
|
||||
/// pr_info!("a: {}", &*foo.a.lock());
|
||||
/// # Ok::<_, AllocError>(())
|
||||
/// ```
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// A normal `let` binding with optional type annotation. The expression is expected to implement
|
||||
/// [`PinInit`]/[`Init`]. This macro assigns a result to the given variable, adding a `?` after the
|
||||
/// `=` will propagate this error.
|
||||
#[macro_export]
|
||||
macro_rules! stack_try_pin_init {
|
||||
(let $var:ident $(: $t:ty)? = $val:expr) => {
|
||||
let val = $val;
|
||||
let mut $var = ::core::pin::pin!($crate::init::__internal::StackInit$(::<$t>)?::uninit());
|
||||
let mut $var = $crate::init::__internal::StackInit::init($var, val);
|
||||
};
|
||||
(let $var:ident $(: $t:ty)? =? $val:expr) => {
|
||||
let val = $val;
|
||||
let mut $var = ::core::pin::pin!($crate::init::__internal::StackInit$(::<$t>)?::uninit());
|
||||
let mut $var = $crate::init::__internal::StackInit::init($var, val)?;
|
||||
};
|
||||
}
|
||||
|
||||
/// Construct an in-place, pinned initializer for `struct`s.
|
||||
///
|
||||
/// This macro defaults the error to [`Infallible`]. If you need [`Error`], then use
|
||||
@ -913,8 +1041,8 @@ macro_rules! try_init {
|
||||
/// A pin-initializer for the type `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).
|
||||
///
|
||||
@ -949,9 +1077,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).
|
||||
///
|
||||
|
@ -112,6 +112,63 @@ unsafe impl<T: ?Sized> HasInitData for T {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stack initializer helper type. Use [`stack_pin_init`] instead of this primitive.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// If `self.is_init` is true, then `self.value` is initialized.
|
||||
///
|
||||
/// [`stack_pin_init`]: kernel::stack_pin_init
|
||||
pub struct StackInit<T> {
|
||||
value: MaybeUninit<T>,
|
||||
is_init: bool,
|
||||
}
|
||||
|
||||
impl<T> Drop for StackInit<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.is_init {
|
||||
// SAFETY: As we are being dropped, we only call this once. And since `self.is_init` is
|
||||
// true, `self.value` is initialized.
|
||||
unsafe { self.value.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 {
|
||||
value: MaybeUninit::uninit(),
|
||||
is_init: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the contents and returns the result.
|
||||
#[inline]
|
||||
pub fn init<E>(self: Pin<&mut Self>, init: impl PinInit<T, E>) -> Result<Pin<&mut T>, E> {
|
||||
// SAFETY: We never move out of `this`.
|
||||
let this = unsafe { Pin::into_inner_unchecked(self) };
|
||||
// The value is currently initialized, so it needs to be dropped before we can reuse
|
||||
// the memory (this is a safety guarantee of `Pin`).
|
||||
if this.is_init {
|
||||
this.is_init = false;
|
||||
// SAFETY: `this.is_init` was true and therefore `this.value` is initialized.
|
||||
unsafe { this.value.assume_init_drop() };
|
||||
}
|
||||
// SAFETY: The memory slot is valid and this type ensures that it will stay pinned.
|
||||
unsafe { init.__pinned_init(this.value.as_mut_ptr())? };
|
||||
// INVARIANT: `this.value` is initialized above.
|
||||
this.is_init = true;
|
||||
// SAFETY: The slot is now pinned, since we will never give access to `&mut T`.
|
||||
Ok(unsafe { Pin::new_unchecked(this.value.assume_init_mut()) })
|
||||
}
|
||||
}
|
||||
|
||||
/// When a value of this type is dropped, it drops a `T`.
|
||||
///
|
||||
/// Can be forgotten to prevent the drop.
|
||||
|
Loading…
Reference in New Issue
Block a user