Rust in Everything
基于 parking_lot 0.12.5

Condvar::wait 报 E0308:expected MutexGuard, found MappedMutexGuard

主线位置:主线 2 的入口。wait 要在桶锁里登记 mutex 地址、在 before_sleep 里放锁、醒来后重新加锁,requeue 时还要直接改 RawMutex 的状态字节(Condvar)。所以它的参数类型钉死在 parking_lot 自己的 MutexGuard 上。

flowchart TB
  g["m.lock():MutexGuard<RawMutex, S>"] -->|"cv.wait(&mut g)"| ok["编译通过"]
  g -->|"MutexGuard::map 到 ready 字段"| mg["MappedMutexGuard:只剩 &R 和数据指针"]
  mg -->|"cv.wait(&mut mg)"| e1["E0308"]
  f["FairMutex::lock():MutexGuard<RawFairMutex, S>"] -->|"cv.wait(&mut g)"| e2["E0308"]
  g -->|"先 wait_while,返回后再 map"| fix["编译运行正常"]

报错原文

exp-parking_lot/mapped-guard-condvar,cargo build --offline --release(registry 路径已缩短)。

映射守卫:

let mut g = MutexGuard::map(m.lock(), |s| &mut s.ready);
cv.wait(&mut g);
error[E0308]: mismatched types
   --> src/bin/mapped.rs:8:11
    |
  8 |   cv.wait(&mut g);
    |      ---- ^^^^^^ expected `&mut MutexGuard<'_, RawMutex, _>`, found `&mut MappedMutexGuard<'_, _, bool>`
    |      |
    |      arguments to this method are incorrect
    |
    = note: expected mutable reference `&mut parking_lot::lock_api::MutexGuard<'_, parking_lot::RawMutex, _>`
               found mutable reference `&mut parking_lot::lock_api::MappedMutexGuard<'_, parking_lot::RawMutex, bool>`
note: method defined here
   --> parking_lot-0.12.5/src/condvar.rs:255:12

FairMutex 的守卫:

error[E0308]: mismatched types
   --> src/bin/fair.rs:7:11
    |
  7 |   cv.wait(&mut g);
    |      ---- ^^^^^^ expected `&mut MutexGuard<'_, RawMutex, _>`, found `&mut MutexGuard<'_, RawFairMutex, bool>`

反向:ok.rs 先在 MutexGuard 上 wait_while,返回后再 MutexGuard::map,编译运行正常,输出 ok: n = 42。

源码出处

  • condvar.rs:254-257:pub fn wait<T: ?Sized>(&self, mutex_guard: &mut MutexGuard<'_, T>),这里的 MutexGuard<'_, T> 是 parking_lot 的别名 lock_api::MutexGuard<'_, RawMutex, T>,raw 类型固定为 RawMutex。
  • 映射守卫拿不回原来的 Mutex:MappedMutexGuard 只存 raw: &R 和 data: *mut T,文档说它「doesn't support temporarily unlocking and re-locking, since that could introduce soundness issues if the locked object is modified by another thread」(lock_api/src/mutex.rs:868-890)。wait 恰好要「临时放开再加锁」,期间别人可能把映射指向的那块数据挪走。
  • FairMutex<T> 是 lock_api::Mutex<RawFairMutex, T>(fair_mutex.rs:77),raw 类型不同,守卫是另一个类型。Condvar 的 requeue 要调 RawMutex 的 mark_parked_if_locked / mark_parked(pub(crate)),对别的 raw 锁无从下手(RawMutex 的一字节状态机)。

怎么改

  • 先等再映射:

    let mut g = m.lock();
    cv.wait_while(&mut g, |s| !s.ready);
    let n = MutexGuard::map(g, |s| &mut s.n);
    
  • 要的只是一个字段,就让条件谓词读那个字段,不必映射。

  • 既要公平又要条件变量:parking_lot 0.12.5 里没有现成组合。可以用普通 Mutex 配 Condvar,需要公平的地方在守卫上显式调 MutexGuard::unlock_fair / bump(公平解锁、bump 与 FairMutex)。

  • 0.13 把 Condvar 移进了 lock_api,变成泛型 Condvar + RawCondvar(0.13 的变化);本页只描述 0.12.5。

小结

  • Condvar::wait 的参数类型固定为 MutexGuard<'_, RawMutex, T>。
  • MappedMutexGuard 和 FairMutex 的守卫都是 E0308;前者是因为映射守卫不允许临时放锁,后者是 raw 类型不同、requeue 无法改它的状态。
  • 改法:先在原守卫上 wait_while,再 map;要公平就在普通 Mutex 上显式公平解锁。

💬 关联讨论

发起讨论
加载中...