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上显式公平解锁。
💬 关联讨论
发起讨论加载中...