为什么Mutex包含Box?

tre*_*tcl 10 mutex rust

Rust std::sync::Mutex是一个包含堆分配的inner互斥锁的结构,以及这个半神秘的注释:

pub struct Mutex<T: ?Sized> {
    // Note that this mutex is in a *box*, not inlined into the struct itself.
    // Once a native mutex has been used once, its address can never change (it
    // can't be moved). This mutex type can be safely moved at any time, so to
    // ensure that the native mutex is used correctly we box the inner mutex to
    // give it a constant address.
    inner: Box<sys::Mutex>,
    poison: poison::Flag,
    data: UnsafeCell<T>,
}
Run Code Online (Sandbox Code Playgroud)

注释解释说,Box它用于为内部互斥锁提供稳定的地址.但我似乎无法找到任何解释为什么首先需要一个稳定的地址.

至少在类Unix平台上,"native mutex"here(sys::Mutex)最终是一个包装器libc::pthread_mutex_t(源代码).

在C中,有一个反对移动互斥体的规则几乎是有意义的,因为互斥体是通过指针使用的,而移动一个,而有一个指向它的实时指针显然是错误的.但在锈,你甚至不能尝试,除非有移动的东西它没有活引用.所以这一论点似乎并不令人信服.

为什么原生互斥锁必须有一个稳定的地址?

Jac*_*nor 9

我认为这里的答案本质上只是“pthread_mutex_t是一种不透明类型”。POSIX 标准不保证它是可移动的,所以我们不能对此做出任何假设。Rust 库团队的 Mara Bos在 Twitter 帖子中讨论了这一点

C 型有多种可能无法移动。它可能包含一个自引用指针,即它的一个字段可能指向另一个字段。这在 Rust 中通常是不允许的,但在 C 和 C++ 中是允许的,并且一些现实世界的类型将这样做作为一种优化。(例如,GCC 的实现就是std::string 这样做的。)C 类型可能做的另一件事是将指向自身的指针插入到某个全局注册表中。在这两种情况下,移动或复制此类类型的实例都会导致悬空指针和其他不一致。

一些(大多数?)实现pthread_mutex_t不执行这些操作。但 POSIX 标准并不禁止它们,任何给定的实现都可能在将来开始执行这些操作而不发出警告。Rust 标准库必须对此采取防御措施,这就是为什么Mutex总是需要BoxUnix 上的标准库。(请注意,Mutex自编写此问题以来,情况发生了一些变化。它不再包含BoxWindows 或 Linux 上的 a,但在其他 Unix 变体上包含 a。)

顺便说一句,futex系统调用(这就是pthread_mutex_tLinux 上的实现方式)也将锁的地址作为参数。这种事情是另一个pthread_mutex_t不可动摇的普遍原因。然而,这个特殊问题与 Rust 无关,因为MutexRust 中的 a 总是在锁定时被借用,这意味着它只能在解锁时移动。


Col*_*Two 5

从 Rust 1.62.0(2022 年 6 月 20 日发布)开始,Mutex在 Linux 系统上不需要堆分配Box或任何其他堆分配。

宣布更改的博客文章确实同意 Jack O'Connor 的答案 - 堆分配已完成,因为无法pthread_mutex_t移动。现在,Mutex不使用pthreads并使用底层Linux futex系统调用。