lock_api
前面几部分讲的都是「一把锁怎么停车、怎么交接」,也就是 parking_lot 自己的 RawMutex / RawRwLock。用户拿到手的 parking_lot::Mutex<T> 却不是 parking_lot 定义的类型,而是 src/mutex.rs 第 86 行 的一个类型别名:
pub type Mutex<T> = lock_api::Mutex<RawMutex, T>;
RwLock、ReentrantMutex、FairMutex 以及所有守卫类型也都是这样的别名。数据放在哪、守卫怎么借出 &mut T、map 之后还能做什么、守卫能不能送到别的线程,全在 lock_api 这个 crate 里,和 parking_lot 的停车场无关。lock_api 是 #![no_std],不依赖 parking_lot_core(源码地图),所以 spin 这类不停车的锁也能用它。
flowchart TB
subgraph user["用户看到的类型(lock_api)"]
mtx["Mutex<R, T>:raw + UnsafeCell<T>"]
grd["MutexGuard / MappedMutexGuard / ArcMutexGuard"]
end
subgraph contract["契约(lock_api 的 unsafe trait)"]
rm["RawMutex:INIT、GuardMarker、lock、try_lock、unsafe unlock"]
ext["RawMutexFair / RawMutexTimed / RawRwLock* / GetThreadId"]
end
subgraph impls["实现者"]
plr["parking_lot::RawMutex(停车场)"]
spn["spin::Mutex<()>(自旋)"]
own["你自己的 RawSpinlock"]
end
mtx --> rm
grd --> rm
rm --> plr
rm --> spn
rm --> own
ext --> plr
| 层 | 回答的问题 | 写在哪 | 由谁保证 |
|---|---|---|---|
RawMutex 等 trait | 「一把原始锁」能做哪些操作 | unsafe trait 的文档 | 实现者(unsafe impl) |
Mutex<R, T> 外壳 | 数据和锁放在一起,只能经守卫访问 | lock_api/src/mutex.rs | lock_api 本身 |
| 守卫 | 什么时候解锁、能不能 map / unlocked / 跨线程 | 同上,GuardMarker 决定 Send | lock_api + 实现者选的标记 |
四章:
- raw trait 与契约:
RawMutex的六个成员;为什么 trait 和unlock都是unsafe;RawMutexFair/RawMutexTimed;RwLock 的 10 个 trait 一张表;GetThreadId;no_std与atomic_usizefeature。 - 外壳与守卫:
Mutex<R, T>的两个字段;MutexGuard只存一个引用;map之后为什么不能unlocked/bump;ArcMutexGuard;RwLock 的 8 种守卫;serde、#[clippy::has_significant_drop]。 - GuardSend 与 send_guard:
GuardNoSend(*mut ())怎么让守卫变成!Send;send_guard只改一个类型别名;与deadlock_detection互斥;在 tokio 里持锁跨.await的编译错误和运行时卡死(E9)。 - 给自己的锁套外壳:文档里的自旋锁三步走,编译并测通;spin 0.9.9 的
lock_apifeature 是同一种写法;外壳不能替你保证的东西。
0.13 起 lock_api 升到 0.5.0:GuardMarker 换成每次加锁的状态 Guard,unlock 要把它交回去,Condvar 也搬进了 lock_api,见 延后唤醒与守卫状态。本部分只写 0.4.14。
本部分的实验
实验目录在 exp-parking_lot/ 下,机器是 M2 Max(8 性能核 + 4 能效核),macOS,rustc 1.99.1,一律 --offline --release。两组都是编译期 / 正确性实验,不计时。
| # | 目录 | 看什么 | 章 |
|---|---|---|---|
| E9 | guard-await/ | ① 三种 feature 组合的 cargo check:默认报 *mut () 不是 Send,开 send_guard 通过,两个 feature 同开 compile_error!;② 开 send_guard 后,current_thread 运行时里持锁跨 .await 卡死 3/3;反向:tokio::sync::Mutex 和 2 个 worker 的多线程运行时都正常结束 | guard-send |
| — | custom-lock/ | lock_api 文档里的自旋锁:8 线程各加 10 万次,计数恰好 80 万;用原子计数器测临界区重叠,正确的锁 0 次,故意坏掉的锁 7 万多次;把标记换成 GuardNoSend 后「守卫是 Send」的断言编译失败 | custom-lock、guard-send |
💬 关联讨论
发起讨论加载中...