|
| 1 | +[](https://crates.io/crates/pin-init) |
| 2 | +[](https://docs.rs/pin-init/) |
| 3 | +[](https://deps.rs/repo/github/Rust-for-Linux/pin-init) |
| 4 | + |
| 5 | +[](#nightly-only) |
| 6 | + |
| 7 | +# `pin-init` |
| 8 | + |
| 9 | +<!-- cargo-rdme start --> |
| 10 | + |
| 11 | +Library to safely and fallibly initialize pinned `struct`s using in-place constructors. |
| 12 | + |
| 13 | +[Pinning][pinning] is Rust's way of ensuring data does not move. |
| 14 | + |
| 15 | +It also allows in-place initialization of big `struct`s that would otherwise produce a stack |
| 16 | +overflow. |
| 17 | + |
| 18 | +This library's main use-case is in [Rust-for-Linux]. Although this version can be used |
| 19 | +standalone. |
| 20 | + |
| 21 | +There are cases when you want to in-place initialize a struct. For example when it is very big |
| 22 | +and moving it from the stack is not an option, because it is bigger than the stack itself. |
| 23 | +Another reason would be that you need the address of the object to initialize it. This stands |
| 24 | +in direct conflict with Rust's normal process of first initializing an object and then moving |
| 25 | +it into it's final memory location. For more information, see |
| 26 | +<https://rust-for-linux.com/the-safe-pinned-initialization-problem>. |
| 27 | + |
| 28 | +This library allows you to do in-place initialization safely. |
| 29 | + |
| 30 | +### Nightly Needed for `alloc` feature |
| 31 | + |
| 32 | +This library requires the [`allocator_api` unstable feature] when the `alloc` feature is |
| 33 | +enabled and thus this feature can only be used with a nightly compiler. When enabling the |
| 34 | +`alloc` feature, the user will be required to activate `allocator_api` as well. |
| 35 | + |
| 36 | +[`allocator_api` unstable feature]: https://doc.rust-lang.org/nightly/unstable-book/library-features/allocator-api.html |
| 37 | + |
| 38 | +The feature is enabled by default, thus by default `pin-init` will require a nightly compiler. |
| 39 | +However, using the crate on stable compilers is possible by disabling `alloc`. In practice this |
| 40 | +will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std |
| 41 | +mode. |
| 42 | + |
| 43 | +## Overview |
| 44 | + |
| 45 | +To initialize a `struct` with an in-place constructor you will need two things: |
| 46 | +- an in-place constructor, |
| 47 | +- a memory location that can hold your `struct` (this can be the [stack], an [`Arc<T>`], |
| 48 | + [`Box<T>`] or any other smart pointer that supports this library). |
| 49 | + |
| 50 | +To get an in-place constructor there are generally three options: |
| 51 | +- directly creating an in-place constructor using the [`pin_init!`] macro, |
| 52 | +- a custom function/macro returning an in-place constructor provided by someone else, |
| 53 | +- using the unsafe function [`pin_init_from_closure()`] to manually create an initializer. |
| 54 | + |
| 55 | +Aside from pinned initialization, this library also supports in-place construction without |
| 56 | +pinning, the macros/types/functions are generally named like the pinned variants without the |
| 57 | +`pin_` prefix. |
| 58 | + |
| 59 | +## Examples |
| 60 | + |
| 61 | +Throughout the examples we will often make use of the `CMutex` type which can be found in |
| 62 | +`../examples/mutex.rs`. It is essentially a userland rebuild of the `struct mutex` type from |
| 63 | +the Linux kernel. It also uses a wait list and a basic spinlock. Importantly the wait list |
| 64 | +requires it to be pinned to be locked and thus is a prime candidate for using this library. |
| 65 | + |
| 66 | +### Using the [`pin_init!`] macro |
| 67 | + |
| 68 | +If you want to use [`PinInit`], then you will have to annotate your `struct` with |
| 69 | +`#[`[`pin_data`]`]`. It is a macro that uses `#[pin]` as a marker for |
| 70 | +[structurally pinned fields]. After doing this, you can then create an in-place constructor via |
| 71 | +[`pin_init!`]. The syntax is almost the same as normal `struct` initializers. The difference is |
| 72 | +that you need to write `<-` instead of `:` for fields that you want to initialize in-place. |
| 73 | + |
| 74 | +```rust |
| 75 | +use pin_init::{pin_data, pin_init, InPlaceInit}; |
| 76 | + |
| 77 | +#[pin_data] |
| 78 | +struct Foo { |
| 79 | + #[pin] |
| 80 | + a: CMutex<usize>, |
| 81 | + b: u32, |
| 82 | +} |
| 83 | + |
| 84 | +let foo = pin_init!(Foo { |
| 85 | + a <- CMutex::new(42), |
| 86 | + b: 24, |
| 87 | +}); |
| 88 | +``` |
| 89 | + |
| 90 | +`foo` now is of the type [`impl PinInit<Foo>`]. We can now use any smart pointer that we like |
| 91 | +(or just the stack) to actually initialize a `Foo`: |
| 92 | + |
| 93 | +```rust |
| 94 | +let foo: Result<Pin<Box<Foo>>, AllocError> = Box::pin_init(foo); |
| 95 | +``` |
| 96 | + |
| 97 | +For more information see the [`pin_init!`] macro. |
| 98 | + |
| 99 | +### Using a custom function/macro that returns an initializer |
| 100 | + |
| 101 | +Many types that use this library supply a function/macro that returns an initializer, because |
| 102 | +the above method only works for types where you can access the fields. |
| 103 | + |
| 104 | +```rust |
| 105 | +let mtx: Result<Pin<Arc<CMutex<usize>>>, _> = Arc::pin_init(CMutex::new(42)); |
| 106 | +``` |
| 107 | + |
| 108 | +To declare an init macro/function you just return an [`impl PinInit<T, E>`]: |
| 109 | + |
| 110 | +```rust |
| 111 | +#[pin_data] |
| 112 | +struct DriverData { |
| 113 | + #[pin] |
| 114 | + status: CMutex<i32>, |
| 115 | + buffer: Box<[u8; 1_000_000]>, |
| 116 | +} |
| 117 | + |
| 118 | +impl DriverData { |
| 119 | + fn new() -> impl PinInit<Self, Error> { |
| 120 | + try_pin_init!(Self { |
| 121 | + status <- CMutex::new(0), |
| 122 | + buffer: Box::init(pin_init::zeroed())?, |
| 123 | + }? Error) |
| 124 | + } |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +### Manual creation of an initializer |
| 129 | + |
| 130 | +Often when working with primitives the previous approaches are not sufficient. That is where |
| 131 | +[`pin_init_from_closure()`] comes in. This `unsafe` function allows you to create a |
| 132 | +[`impl PinInit<T, E>`] directly from a closure. Of course you have to ensure that the closure |
| 133 | +actually does the initialization in the correct way. Here are the things to look out for |
| 134 | +(we are calling the parameter to the closure `slot`): |
| 135 | +- when the closure returns `Ok(())`, then it has completed the initialization successfully, so |
| 136 | + `slot` now contains a valid bit pattern for the type `T`, |
| 137 | +- when the closure returns `Err(e)`, then the caller may deallocate the memory at `slot`, so |
| 138 | + you need to take care to clean up anything if your initialization fails mid-way, |
| 139 | +- you may assume that `slot` will stay pinned even after the closure returns until `drop` of |
| 140 | + `slot` gets called. |
| 141 | + |
| 142 | +```rust |
| 143 | +use pin_init::{pin_data, pinned_drop, PinInit, PinnedDrop, pin_init_from_closure}; |
| 144 | +use core::{ |
| 145 | + ptr::addr_of_mut, |
| 146 | + marker::PhantomPinned, |
| 147 | + cell::UnsafeCell, |
| 148 | + pin::Pin, |
| 149 | + mem::MaybeUninit, |
| 150 | +}; |
| 151 | +mod bindings { |
| 152 | + #[repr(C)] |
| 153 | + pub struct foo { |
| 154 | + /* fields from C ... */ |
| 155 | + } |
| 156 | + extern "C" { |
| 157 | + pub fn init_foo(ptr: *mut foo); |
| 158 | + pub fn destroy_foo(ptr: *mut foo); |
| 159 | + #[must_use = "you must check the error return code"] |
| 160 | + pub fn enable_foo(ptr: *mut foo, flags: u32) -> i32; |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +/// # Invariants |
| 165 | +/// |
| 166 | +/// `foo` is always initialized |
| 167 | +#[pin_data(PinnedDrop)] |
| 168 | +pub struct RawFoo { |
| 169 | + #[pin] |
| 170 | + _p: PhantomPinned, |
| 171 | + #[pin] |
| 172 | + foo: UnsafeCell<MaybeUninit<bindings::foo>>, |
| 173 | +} |
| 174 | + |
| 175 | +impl RawFoo { |
| 176 | + pub fn new(flags: u32) -> impl PinInit<Self, i32> { |
| 177 | + // SAFETY: |
| 178 | + // - when the closure returns `Ok(())`, then it has successfully initialized and |
| 179 | + // enabled `foo`, |
| 180 | + // - when it returns `Err(e)`, then it has cleaned up before |
| 181 | + unsafe { |
| 182 | + pin_init_from_closure(move |slot: *mut Self| { |
| 183 | + // `slot` contains uninit memory, avoid creating a reference. |
| 184 | + let foo = addr_of_mut!((*slot).foo); |
| 185 | + let foo = UnsafeCell::raw_get(foo).cast::<bindings::foo>(); |
| 186 | + |
| 187 | + // Initialize the `foo` |
| 188 | + bindings::init_foo(foo); |
| 189 | + |
| 190 | + // Try to enable it. |
| 191 | + let err = bindings::enable_foo(foo, flags); |
| 192 | + if err != 0 { |
| 193 | + // Enabling has failed, first clean up the foo and then return the error. |
| 194 | + bindings::destroy_foo(foo); |
| 195 | + Err(err) |
| 196 | + } else { |
| 197 | + // All fields of `RawFoo` have been initialized, since `_p` is a ZST. |
| 198 | + Ok(()) |
| 199 | + } |
| 200 | + }) |
| 201 | + } |
| 202 | + } |
| 203 | +} |
| 204 | + |
| 205 | +#[pinned_drop] |
| 206 | +impl PinnedDrop for RawFoo { |
| 207 | + fn drop(self: Pin<&mut Self>) { |
| 208 | + // SAFETY: Since `foo` is initialized, destroying is safe. |
| 209 | + unsafe { bindings::destroy_foo(self.foo.get().cast::<bindings::foo>()) }; |
| 210 | + } |
| 211 | +} |
| 212 | +``` |
| 213 | + |
| 214 | +For more information on how to use [`pin_init_from_closure()`], take a look at the uses inside |
| 215 | +the `kernel` crate. The [`sync`] module is a good starting point. |
| 216 | + |
| 217 | +[`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html |
| 218 | +[pinning]: https://doc.rust-lang.org/std/pin/index.html |
| 219 | +[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field |
| 220 | +[stack]: https://docs.rs/pin-init/latest/pin_init/macro.stack_pin_init.html |
| 221 | +[`Arc<T>`]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html |
| 222 | +[`Box<T>`]: https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html |
| 223 | +[`impl PinInit<Foo>`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html |
| 224 | +[`impl PinInit<T, E>`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html |
| 225 | +[`impl Init<T, E>`]: https://docs.rs/pin-init/latest/pin_init/trait.Init.html |
| 226 | +[Rust-for-Linux]: https://rust-for-linux.com/ |
| 227 | + |
| 228 | +<!-- cargo-rdme end --> |
0 commit comments