【问题标题】:Why does Mutex contain a Box?为什么 Mutex 包含一个 Box?
【发布时间】:2018-07-18 01:10:58
【问题描述】:

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>,
}

评论解释说Box 用于给内部互斥体一个稳定的地址。但我似乎找不到任何解释为什么首先需要一个稳定的地址。

至少在类 Unix 平台上,这里的“本机互斥锁” (sys::Mutex) 最终是 libc::pthread_mutex_t (source code) 的包装。

在 C 中,有一条禁止移动互斥锁的规则几乎是有意义的,因为互斥锁是通过指针使用的,而在有指向它的活动指针时移动它显然是错误的。但是在 Rust 中,你甚至不能尝试移动某些东西,除非 没有对它的实时引用。所以这种说法似乎没有说服力。

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

【问题讨论】:

  • 我想如果 LLVM 复制该值,则可以毫无问题地移动生锈。
  • 还有Can a pthread_mutex_t be moved in memory?。我的重点是:Linux 上的一些互斥锁实现,例如,使用 futex 系统调用,它专门等待 互斥锁的地址
  • @Shepmaster 这似乎是为什么Mutex 不能在锁定时移动的一个很好的理由。但在任何情况下它都不会在锁定时移动,因为MutexGuard 在此期间借用它。 futex 限制是否也会影响未锁定的互斥锁?
  • @Shepmaster 我认为应该重新讨论这个问题,因为它与关于 std::mutex 的问题并不像起初看起来那样密切相关。具体来说:在 C++ 中,std::mutex 需要担心在它被锁定时被移动。这意味着它需要担心底层系统调用的行为,比如 Linux 上的 futex。 (事实上​​,由于 futex 将锁的地址作为参数,所以我们绝对不能在锁被锁定时移动它。)但是这个特定的问题不适用于 Rust。相反,Rust 担心的是 pthread_mutex_t 可能是自引用的
  • 嗯,我想我同意你所说的,@Jack。你会写那个答案吗?

标签: rust mutex


【解决方案1】:

我认为这里的答案基本上只是“pthread_mutex_t 是一种不透明的类型”。 POSIX 标准不保证它是可移动的,所以我们不能对它做任何假设。来自 Rust 库团队的 Mara Bos discussed this in a Twitter thread。

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

pthread_mutex_t 的一些(大多数?)实现不做这些事情。但是 POSIX 标准并没有禁止它们,并且任何给定的实现都可以在将来毫无警告地开始做这些事情。 Rust 标准库必须对此采取防御措施,这就是为什么 Mutex 在 Unix 上总是需要 Box 的原因。 (请注意,Mutex 在编写此问题后发生了一些变化。在 Windows 上它不再包含 Box。)

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-12
    • 2012-09-15
    • 1970-01-01
    • 2015-11-04
    • 2013-11-20
    相关资源
    最近更新 更多