Expand description
API to safely and fallibly initialize pinned struct
s using in-place constructors.
It also allows in-place initialization of big struct
s that would otherwise produce a stack
overflow.
Most struct
s from the sync
module need to be pinned, because they contain self-referential
struct
s from C. Pinning is Rust’s way of ensuring data does not move.
Overview
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
(this can be the stack, anArc<T>
,UniqueArc<T>
,Box<T>
or any other smart pointer that implementsInPlaceInit
).
To get an in-place constructor there are generally three options:
- directly creating an in-place constructor using the
pin_init!
macro, - a custom function/macro returning an in-place constructor provided by someone else,
- using the unsafe function
pin_init_from_closure()
to manually create an initializer.
Aside from pinned initialization, this API also supports in-place construction without pinning,
the macros/types/functions are generally named like the pinned variants without the pin
prefix.
Examples
Using the pin_init!
macro
If you want to use PinInit
, then you will have to annotate your struct
with
#[
pin_data
]
. It is a macro that uses #[pin]
as a marker for
structurally pinned fields. After doing this, you can then create an in-place constructor via
pin_init!
. The syntax is almost the same as normal struct
initializers. The difference is
that you need to write <-
instead of :
for fields that you want to initialize in-place.
use kernel::{prelude::*, sync::Mutex, new_mutex};
#[pin_data]
struct Foo {
#[pin]
a: Mutex<usize>,
b: u32,
}
let foo = pin_init!(Foo {
a <- new_mutex!(42, "Foo::a"),
b: 24,
});
foo
now is of the type impl PinInit<Foo>
. We can now use any smart pointer that we like
(or just the stack) to actually initialize a Foo
:
let foo: Result<Pin<Box<Foo>>> = Box::pin_init(foo);
For more information see the pin_init!
macro.
Using a custom function/macro that returns an initializer
Many types from the kernel supply a function/macro that returns an initializer, because the above method only works for types where you can access the fields.
let mtx: Result<Arc<Mutex<usize>>> = Arc::pin_init(new_mutex!(42, "example::mtx"));
To declare an init macro/function you just return an impl PinInit<T, E>
:
#[pin_data]
struct DriverData {
#[pin]
status: Mutex<i32>,
buffer: Box<[u8; 1_000_000]>,
}
impl DriverData {
fn new() -> impl PinInit<Self, Error> {
try_pin_init!(Self {
status <- new_mutex!(0, "DriverData::status"),
buffer: Box::init(kernel::init::zeroed())?,
})
}
}
Manual creation of an initializer
Often when working with primitives the previous approaches are not sufficient. That is where
pin_init_from_closure()
comes in. This unsafe
function allows you to create a
impl PinInit<T, E>
directly from a closure. Of course you have to ensure that the closure
actually does the initialization in the correct way. Here are the things to look out for
(we are calling the parameter to the closure slot
):
- when the closure returns
Ok(())
, then it has completed the initialization successfully, soslot
now contains a valid bit pattern for the typeT
, - when the closure returns
Err(e)
, then the caller may deallocate the memory atslot
, so you need to take care to clean up anything if your initialization fails mid-way, - you may assume that
slot
will stay pinned even after the closure returns untildrop
ofslot
gets called.
use kernel::{prelude::*, init, types::Opaque};
use core::{ptr::addr_of_mut, marker::PhantomPinned, pin::Pin};
/// # Invariants
///
/// `foo` is always initialized
#[pin_data(PinnedDrop)]
pub struct RawFoo {
#[pin]
foo: Opaque<bindings::foo>,
#[pin]
_p: PhantomPinned,
}
impl RawFoo {
pub fn new(flags: u32) -> impl PinInit<Self, Error> {
// SAFETY:
// - when the closure returns `Ok(())`, then it has successfully initialized and
// enabled `foo`,
// - when it returns `Err(e)`, then it has cleaned up before
unsafe {
init::pin_init_from_closure(move |slot: *mut Self| {
// `slot` contains uninit memory, avoid creating a reference.
let foo = addr_of_mut!((*slot).foo);
// Initialize the `foo`
bindings::init_foo(Opaque::raw_get(foo));
// Try to enable it.
let err = bindings::enable_foo(Opaque::raw_get(foo), flags);
if err != 0 {
// Enabling has failed, first clean up the foo and then return the error.
bindings::destroy_foo(Opaque::raw_get(foo));
return Err(Error::from_errno(err));
}
// All fields of `RawFoo` have been initialized, since `_p` is a ZST.
Ok(())
})
}
}
}
#[pinned_drop]
impl PinnedDrop for RawFoo {
fn drop(self: Pin<&mut Self>) {
// SAFETY: Since `foo` is initialized, destroying is safe.
unsafe { bindings::destroy_foo(self.foo.get()) };
}
}
For the special case where initializing a field is a single FFI-function call that cannot fail,
there exist the helper function Opaque::ffi_init
. This function initialize a single
Opaque
field by just delegating to the supplied closure. You can use these in combination
with pin_init!
.
For more information on how to use pin_init_from_closure()
, take a look at the uses inside
the kernel
crate. The sync
module is a good starting point.
Structs
- An initializer returned by
Init::chain
. - An initializer returned by
PinInit::pin_chain
.
Traits
- Smart pointer that can initialize memory in-place.
- An initializer for
T
. - A pin-initializer for the type
T
. - Trait facilitating pinned destruction.
- Marker trait for types that can be initialized by writing just zeroes.
Functions
- Initializes an array by initializing each element via the provided initializer.
- Creates a new
Init<T, E>
from the given closure. - Initializes an array by initializing each element via the provided initializer.
- Creates a new
PinInit<T, E>
from the given closure. - An initializer that leaves the memory uninitialized.
- Create a new zeroed T.