pin-init
[!NOTE] This crate was originally named
pinned-init
, but the migration topin-init
is not yet complete. Thelegcay
branch contains the current version of thepinned-init
crate & themain
branch already incorporates the rename topin-init
.There are still some changes needed on the kernel side before the migration can be completed.
Library to safely and fallibly initialize pinned struct
s using in-place constructors.
Pinning is Rust's way of ensuring data does not move.
It also allows in-place initialization of big struct
s that would otherwise produce a stack overflow.
This library's main use-case is in Rust-for-Linux. Although this version can be used standalone.
There are cases when you want to in-place initialize a struct. For example when it is very big and moving it from the stack is not an option, because it is bigger than the stack itself. Another reason would be that you need the address of the object to initialize it. This stands in direct conflict with Rust's normal process of first initializing an object and then moving it into it's final memory location. For more information, see https://rust-for-linux.com/the-safe-pinned-initialization-problem.
This library allows you to do in-place initialization safely.
Nightly Needed for alloc
feature
This library requires the allocator_api
unstable feature when the alloc
feature is enabled and thus this feature can only be used with a nightly compiler. When enabling the alloc
feature, the user will be required to activate allocator_api
as well.
The feature is enabled by default, thus by default pin-init
will require a nightly compiler. However, using the crate on stable compilers is possible by disabling alloc
. In practice this will require the std
feature, because stable compilers have neither Box
nor Arc
in no-std mode.
Nightly needed for unsafe-pinned
feature
This feature enables the Wrapper
implementation on the unstable core::pin::UnsafePinned
type. This requires the unsafe_pinned
unstable feature and therefore a nightly compiler. Note that this feature is not enabled by default.
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>
,Box<T>
or any other smart pointer that supports this library).
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 library also supports in-place construction without pinning, the macros/types/functions are generally named like the pinned variants without the pin_
prefix.
Examples
Throughout the examples we will often make use of the CMutex
type which can be found in ../examples/mutex.rs
. It is essentially a userland rebuild of the struct mutex
type from the Linux kernel. It also uses a wait list and a basic spinlock. Importantly the wait list requires it to be pinned to be locked and thus is a prime candidate for using this library.
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.
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
:
For more information see the [pin_init!
] macro.
Using a custom function/macro that returns an initializer
Many types that use this library supply a function/macro that returns an initializer, because the above method only works for types where you can access the fields.
To declare an init macro/function you just return an impl PinInit<T, E>
:
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.
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.