6 个版本

0.0.8 2024 年 7 月 7 日
0.0.7 2024 年 4 月 9 日
0.0.6 2023 年 4 月 8 日
0.0.5 2023 年 3 月 27 日
0.0.0 2022 年 4 月 18 日

#159 in Rust 模式

Download history 13/week @ 2024-05-18 2/week @ 2024-05-25 156/week @ 2024-07-06 8/week @ 2024-07-13

505 每月下载量
用于 owned-pin

MIT/Apache

125KB
1K SLoC

Crates.io Documentation Dependency status License Toolchain GitHub Workflow Status

Pinned-init

使用原地构造函数安全地初始化固定 struct 的库。

固定 是 Rust 确保数据不移动的方法。

它还允许原地初始化大型 struct,否则会产生栈溢出。

此库的主要用例在 Rust-for-Linux 中。尽管此版本可以单独使用。

有些情况下,您希望原地初始化一个结构体。例如,当它非常大,无法将其从栈上移动,因为它的体积大于栈本身时。另一个原因可能是您需要对象的地址来初始化它。这与 Rust 的正常过程直接冲突,即首先初始化对象,然后将其移动到最终的内存位置。

此库允许您安全地进行原地初始化。

对于 allocstd 功能需要夜间构建

当启用 allocstd 功能时,此库需要不稳定的功能,因此只能与夜间编译器一起使用。内部使用的功能包括

  • allocator_api
  • new_uninit
  • get_mut_unchecked

当启用 allocstd 功能时,用户将需要激活这些功能

  • allocator_api

概述

要使用原地构造函数初始化结构体,您需要以下两个条件

  • 一个原地构造函数,
  • 一个可以存储您的 struct 的内存位置(这可以是 、一个 Arc<T>Box<T> 或任何实现了 InPlaceInit 的智能指针)。

要获得就地构造函数,通常有三种选择

  • 直接使用 pin_init! 宏创建就地构造函数,
  • 一个自定义函数/宏,由其他人提供就地构造函数,
  • 使用不安全的函数 [pin_init_from_closure()] 手动创建初始化器。

除了固定初始化之外,此库还支持不带固定的就地构造,宏/类型/函数的命名通常与固定变体相同,但没有 pin 前缀。

示例

通过一些示例,我们将使用 CMutex 类型,该类型可在 ../examples/mutex.rs 中找到。它本质上是在用户空间中对 Linux 内核中的 mutex 的重建。因此,它也使用等待列表和基本的自旋锁。重要的是,它需要固定才能被锁定,因此是使用此库的理想选择。

使用 pin_init!

如果您想使用 PinInit,那么您将不得不使用 #[pin_data] 注解您的 struct。这是一个使用 #[pin] 作为结构固定字段的标记的宏。完成此操作后,您可以通过 pin_init! 创建就地构造函数。语法几乎与常规 struct 初始化器相同。区别在于,对于您想要就地初始化的字段,您需要编写 <- 而不是 :

use pinned_init::*;
#[pin_data]
struct Foo {
    #[pin]
    a: CMutex<usize>,
    b: u32,
}

let foo = pin_init!(Foo {
    a <- CMutex::new(42),
    b: 24,
});

foo 现在是 impl PinInit<Foo> 的类型。我们现在可以使用任何我们喜欢的智能指针(或者只是栈)来实际初始化一个 Foo

let foo: Result<Pin<Box<Foo>>, _> = Box::pin_init(foo);

有关更多信息,请参阅 pin_init! 宏。

使用返回初始化器的自定义函数/宏

许多使用此库的类型提供了一个返回初始化器的函数/宏,因为上述方法仅适用于可以访问字段类型的类型。

let mtx: Result<Pin<Arc<CMutex<usize>>>, _> = Arc::pin_init(CMutex::new(42));

要声明一个初始化宏/函数,您只需返回一个 impl PinInit<T, E>

#[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(pinned_init::zeroed())?,
        }? Error)
    }
}

手动创建初始化器

通常在与原始类型一起工作时,前面的方法并不足够。这时,[pin_init_from_closure()] 就派上用场。这个 unsafe 函数允许您从闭包直接创建一个 impl PinInit<T, E>。当然,您必须确保闭包确实以正确的方式进行了初始化。以下是需要注意的事项(我们称闭包的参数为 slot

  • 当闭包返回 Ok(()) 时,它已成功完成初始化,因此 slot 现在包含类型 T 的有效位模式。
  • 当闭包返回 Err(e) 时,调用者可能需要释放 slot 的内存,因此如果初始化过程中出现失败,您需要确保进行清理。
  • 您可能假设即使在闭包返回后,slot 也会保持固定,直到调用 drop slot
use pinned_init::*;
use core::{ptr::addr_of_mut, marker::PhantomPinned, cell::UnsafeCell, pin::Pin};
mod bindings {
    extern "C" {
        pub type foo;
        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<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);

                // Initialize the `foo`
                bindings::init_foo(UnsafeCell::raw_get(foo));

                // Try to enable it.
                let err = bindings::enable_foo(UnsafeCell::raw_get(foo), flags);
                if err != 0 {
                    // Enabling has failed, first clean up the foo and then return the error.
                    bindings::destroy_foo(UnsafeCell::raw_get(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()) };
    }
}

有关如何使用 [pin_init_from_closure()] 的更多信息,请查看 kernel 包内的用法。 sync 模块是一个好的起点。

依赖项

~88KB