1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
|
[](https://crates.io/crates/pin-init)
[](https://docs.rs/pin-init/)
[](https://deps.rs/repo/github/Rust-for-Linux/pin-init)

[](#nightly-only)

# `pin-init`
<!-- cargo-rdme start -->
Library to safely and fallibly initialize pinned `struct`s using in-place constructors.
[Pinning][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.
[`allocator_api` unstable feature]: https://doc.rust-lang.org/nightly/unstable-book/library-features/allocator-api.html
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.
## 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], an [`Arc<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.
```rust
use pin_init::{pin_data, pin_init, InPlaceInit};
#[pin_data]
struct Foo {
#[pin]
a: CMutex<usize>,
b: u32,
}
let foo = pin_init!(Foo {
a <- CMutex::new(42),
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`:
```rust
let foo: Result<Pin<Box<Foo>>, AllocError> = Box::pin_init(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.
```rust
let mtx: Result<Pin<Arc<CMutex<usize>>>, _> = Arc::pin_init(CMutex::new(42));
```
To declare an init macro/function you just return an [`impl PinInit<T, E>`]:
```rust
#[pin_data]
struct DriverData {
#[pin]
status: CMutex<i32>,
buffer: Box<[u8; 1_000_000]>,
}
impl DriverData {
fn new() -> impl PinInit<Self, Error> {
try_pin_init!(Self {
status <- CMutex::new(0),
buffer: Box::init(pin_init::zeroed())?,
}? Error)
}
}
```
### 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, so
`slot` now contains a valid bit pattern for the type `T`,
- when the closure returns `Err(e)`, then the caller may deallocate the memory at `slot`, 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 until `drop` of
`slot` gets called.
```rust
use pin_init::{pin_data, pinned_drop, PinInit, PinnedDrop, pin_init_from_closure};
use core::{
ptr::addr_of_mut,
marker::PhantomPinned,
cell::UnsafeCell,
pin::Pin,
mem::MaybeUninit,
};
mod bindings {
#[repr(C)]
pub struct foo {
/* fields from C ... */
}
extern "C" {
pub fn init_foo(ptr: *mut foo);
pub fn destroy_foo(ptr: *mut foo);
#[must_use = "you must check the error return code"]
pub fn enable_foo(ptr: *mut foo, flags: u32) -> i32;
}
}
/// # Invariants
///
/// `foo` is always initialized
#[pin_data(PinnedDrop)]
pub struct RawFoo {
#[pin]
_p: PhantomPinned,
#[pin]
foo: UnsafeCell<MaybeUninit<bindings::foo>>,
}
impl RawFoo {
pub fn new(flags: u32) -> impl PinInit<Self, i32> {
// 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 {
pin_init_from_closure(move |slot: *mut Self| {
// `slot` contains uninit memory, avoid creating a reference.
let foo = addr_of_mut!((*slot).foo);
let foo = UnsafeCell::raw_get(foo).cast::<bindings::foo>();
// Initialize the `foo`
bindings::init_foo(foo);
// Try to enable it.
let err = bindings::enable_foo(foo, flags);
if err != 0 {
// Enabling has failed, first clean up the foo and then return the error.
bindings::destroy_foo(foo);
Err(err)
} else {
// 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().cast::<bindings::foo>()) };
}
}
```
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.
[`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html
[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]: https://docs.rs/pin-init/latest/pin_init/macro.stack_pin_init.html
[`Arc<T>`]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html
[`Box<T>`]: https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html
[`impl PinInit<Foo>`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html
[`impl PinInit<T, E>`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html
[`impl Init<T, E>`]: https://docs.rs/pin-init/latest/pin_init/trait.Init.html
[Rust-for-Linux]: https://rust-for-linux.com/
<!-- cargo-rdme end -->
|