源码地图
主线位置:四条主线经过的 crate 和文件。主线 1(有竞争的加锁)从 parking_lot 的
raw_mutex.rs落到 core 的parking_lot.rs;主线 2(notify_all)从condvar.rs落到同一个文件的unpark_requeue;主线 3(停车场的一生)和主线 4(桶锁与内存序)全在 core 里。
flowchart BT smallvec["smallvec"] cfgif["cfg-if"] libc["libc(unix)/ windows-link / redox_syscall"] pg["petgraph + backtrace(只在 deadlock_detection 时)"] core["parking_lot_core 0.9.12:停车场、WordLock、ThreadParker、SpinWait、死锁检测"] --> smallvec core --> cfgif core --> libc core -.-> pg sg["scopeguard"] api["lock_api 0.4.14:raw trait、Mutex / RwLock / ReentrantMutex 外壳与守卫(no_std)"] --> sg pl["parking_lot 0.12.5:RawMutex、RawRwLock、Condvar、Once、FairMutex、ReentrantMutex"] --> core pl --> api user["用户代码"] --> pl other["spin 等第三方锁"] --> api
三层 crate
| crate | 版本 | 依赖 | src/ 行数 | 本卷 |
|---|---|---|---|---|
parking_lot_core | 0.9.12 | cfg-if、smallvec;unix 上 libc,Windows 上 windows-link,redox 上 redox_syscall;可选 petgraph、backtrace | 3687,其中 parking_lot.rs 第 1429 行起约 270 行是测试 | 02 |
lock_api | 0.4.14 | scopeguard;可选 owning_ref、serde | 5425,大部分是文档注释 | 06 |
parking_lot | 0.12.5 | 只有 parking_lot_core 和 lock_api | 5216,其中 condvar.rs 第 525 行起、once.rs 第 327 行起、mutex.rs 第 111 行起是测试 | 03–05 |
行数是 src/ 下 .rs 的总行数,含文档注释与文件内的 #[cfg(test)] 模块。三个 crate 的 MSRV 都是 1.71,edition 2021(三个 Cargo.toml)。
依赖方向有一点值得注意:lock_api 不依赖 parking_lot_core。lock_api 只定义「一把原始锁要提供什么」和外壳,是 #![no_std] 的(lock_api/src/lib.rs:88),任何 crate 都可以拿它包自己的锁,不会被带进停车场。parking_lot 才是把两边接起来的那一层:它的 RawMutex 实现 lock_api 的 RawMutex trait,慢路径调 core 的 park / unpark_one;用户拿到的 parking_lot::Mutex<T> 就是 lock_api::Mutex<parking_lot::RawMutex, T> 的类型别名。
README 也解释了为什么把 core 拆出来:「core API 的改动不会给 parking_lot 的用户带来破坏性变更」(README 第 129–131 行)。所以 core 的版本号(0.9)和 parking_lot(0.12)各走各的。
parking_lot_core:每个文件一句话
| 文件 | 行数 | 管什么 | 本卷 |
|---|---|---|---|
lib.rs | 67 | crate 文档(停车场的来历)、导出 park / unpark_* / SpinWait / deadlock | why-parking-lot |
parking_lot.rs | 1700 | 全局哈希表与扩容、ThreadData、FairTimeout、park 与四个 unpark 函数、ParkToken / UnparkToken / RequeueOp / FilterOp,死锁检测(1095–1427),测试(1429 起) | hashtable、park-protocol、eventual-fairness、deadlock-detection |
word_lock.rs | 327 | 桶锁 WordLock:一个 usize 里两位标志加一个队列头指针,自己管等待队列 | word-lock |
spinwait.rs | 74 | SpinWait:10 步,前 3 步自旋、后 7 步让出 | spinwait |
util.rs | 31 | unchecked_unwrap:release 下是 unreachable_unchecked | — |
thread_parker/mod.rs | 85 | ThreadParkerT / UnparkHandleT 两个 trait,cfg_if 按平台选实现 | thread-parker |
thread_parker/unix.rs | 242 | 非 Linux 的 unix(本机 macOS 走这里):pthread_mutex_t + pthread_cond_t + 一个 Cell<bool> | 同上 |
thread_parker/linux.rs | 156 | Linux / Android:一个 AtomicI32 + FUTEX_WAIT / FUTEX_WAKE | 同上 |
thread_parker/windows/ | 527(4 个文件) | 运行时在 WaitOnAddress 和 NT keyed event 之间选一个 | 同上 |
thread_parker/redox.rs、sgx.rs、wasm_atomic.rs、wasm.rs、generic.rs | 139、109、97、54、79 | 其他平台;wasm.rs 一停车就 panic,generic.rs 是忙等 | 同上(一张表) |
parking_lot:每个文件一句话
| 文件 | 行数 | 管什么 | 本卷 |
|---|---|---|---|
lib.rs | 62 | 导出;send_guard 与 deadlock_detection 互斥的 compile_error!;GuardMarker 类型别名 | guard-send |
raw_mutex.rs | 331 | RawMutex:LOCKED_BIT / PARKED_BIT 两位、lock_slow / unlock_slow / bump_slow,TOKEN_NORMAL / TOKEN_HANDOFF | raw-mutex |
mutex.rs | 385 | Mutex<T> / MutexGuard 类型别名、const_mutex,其余是文档和测试 | 同上 |
raw_fair_mutex.rs | 65 | RawFairMutex(RawMutex):unlock 转 unlock_fair | fair-and-bump |
fair_mutex.rs | 274 | FairMutex<T> 类型别名,其余是文档和测试 | 同上 |
remutex.rs | 171 | RawThreadId:线程局部变量的地址当线程 id;ReentrantMutex<T> 别名 | reentrant |
raw_rwlock.rs | 1158 | RawRwLock:状态字、主队列与 addr + 1 第二队列、读 / 写 / 可升级三种 token、wake_parked_threads | 04 RwLock |
rwlock.rs | 659 | RwLock<T> 与各守卫的类型别名,其余是文档和测试 | 同上 |
elision.rs | 119 | x86 TSX 的 xacquire / xrelease,只在 hardware-lock-elision feature 下用 | state-word |
condvar.rs | 1270 | Condvar{state: AtomicPtr<RawMutex>}、notify_* 的 requeue、wait* | condvar |
once.rs | 452 | 1 字节的 Once:DONE / POISON / LOCKED / PARKED 四位 | once |
deadlock.rs | 232 | 重新导出 check_deadlock,其余是测试 | deadlock-detection |
util.rs | 38 | to_deadline:Duration 转 Instant,溢出时返回 None | — |
lock_api:每个文件一句话
| 文件 | 行数 | 管什么 | 本卷 |
|---|---|---|---|
lib.rs | 117 | crate 文档(自旋锁示例)、GuardSend / GuardNoSend、atomic_usize 控制 remutex 是否编译 | traits、guard-send |
mutex.rs | 1039 | RawMutex / RawMutexFair / RawMutexTimed,Mutex<R, T>,MutexGuard、MappedMutexGuard、ArcMutexGuard | traits、wrappers |
remutex.rs | 1139 | GetThreadId、RawReentrantMutex(计数 + 所有者)、ReentrantMutex 与守卫 | reentrant |
rwlock.rs | 3130 | RwLock 的 9 个 raw trait,RwLock<R, T>,读 / 写 / 可升级三类守卫 × 普通 / Mapped / Arc | wrappers、upgradable |
feature 转发表
parking_lot 自己的 feature 大多只是转发(根 Cargo.toml):
| parking_lot 的 feature | 转发到 | 作用 | 本卷 |
|---|---|---|---|
arc_lock | lock_api/arc_lock | lock_arc() 返回持有 Arc 的守卫 | wrappers |
owning_ref | lock_api/owning_ref | 给守卫实现 owning_ref 的 trait(这个依赖已不维护,一句带过) | 同上 |
serde | lock_api/serde | 序列化锁里的数据,反序列化时新建一把锁 | 同上 |
nightly | parking_lot_core/nightly、lock_api/nightly | core 里只影响 wasm 原子等待后端的选择(thread_parker/mod.rs:69-75);lock_api 的 nightly 在 src/ 里没有任何引用 | thread-parker |
deadlock_detection | parking_lot_core/deadlock_detection | 打开死锁检测,拉进 petgraph、backtrace | deadlock-detection |
send_guard | 本 crate | 把 GuardMarker 从 GuardNoSend 换成 GuardSend(src/lib.rs:32-39) | guard-send |
hardware-lock-elision | 本 crate | x86 上 RwLock 读锁用 TSX;本机 aarch64 上不生效 | state-word |
lock_api 还有一个默认打开的 atomic_usize:关掉后不编译 remutex 模块(lock_api/src/lib.rs:110-113),给没有原子 usize 的平台用。
两个 feature 互斥:send_guard 和 deadlock_detection 同时打开会触发 src/lib.rs:34-35 的
compile_error!("the `send_guard` and `deadlock_detection` features cannot be used together");
Cargo 的 feature 在整个依赖图里统一,所以这条限制可能由两个互不相干的依赖分别触发,见 GuardSend 与 send_guard。
benchmark 与 tests
benchmark/ 是工作区之外的单独 crate(根 Cargo.toml 的 [workspace] exclude),本卷只说它测什么,不运行它:
| 程序 | 行数 | 参数 | 对比对象 |
|---|---|---|---|
benchmark/src/bin/mutex.rs | 311 | 线程数、临界区内工作量、临界区外工作量、每轮秒数、轮数 | parking_lot::Mutex、std::sync::Mutex,Windows 上加 SRWLOCK,其他平台加裸 pthread_mutex_t |
benchmark/src/bin/rwlock.rs | 419 | 写者数、读者数,其余同上 | parking_lot::RwLock、std::sync::RwLock、seqlock::SeqLock,Windows 上加 SRWLOCK,其他平台加 pthread_rwlock_t |
本卷自己的吞吐实验(E2)沿用同样的思路:临界区外的工作量是决定「谁快」的关键参数,见 和 std Mutex 比。
tests/ 只有两个回归测试,都是 RwLock:
| 文件 | 行数 | 测什么 |
|---|---|---|
tests/issue_203.rs | 26 | 在线程局部变量的析构函数里对另一个线程局部的 RwLock 加锁(TLS 析构期间停车场还要能用;对应 with_thread_data 在 TLS 已析构时退回栈上临时 ThreadData 的分支,见 停车场哈希表) |
tests/issue_392.rs | 15 | 同一个可升级读守卫连续调两次 with_upgraded(64b7114「Fix RwLockUpgradeableReadGuard::with_upgraded」) |
README 的 18 条:在哪章核实
README 第 27–72 行列了 18 条「优于标准库」的地方。它是资料,不是结论;本卷逐条对照代码或实测:
| # | README 的说法(大意) | 本卷 |
|---|---|---|
| 1 | Mutex、Once 只要 1 字节,Condvar、RwLock 只要 1 个字;std 在 macOS 等平台要堆分配 | 和 std Mutex 比(E1) |
| 2 | 无竞争的加锁与解锁走内联快路径,只要一次原子操作 | RawMutex 的一字节状态机 |
| 3 | 短临界区的「微竞争」靠自旋几次处理 | SpinWait |
| 4 | 自旋几次失败后挂起线程,长短临界区都适用 | 同上 |
| 5 | Condvar、RwLock、Once 能在 Windows XP 上用 | ThreadParker 的各平台实现 |
| 6 | RwLock 可用硬件锁省略(hardware-lock-elision) | 状态字与两条队列 |
| 7 | RwLock 是 task-fair 的,读者写者都不饿死 | 读、写与写者优先 |
| 8 | Condvar 保证没有虚假唤醒 | Condvar |
| 9 | notify_all 只叫醒一个,其余 requeue 到 mutex 上,避免惊群 | 一次 notify_all、Condvar |
| 10 | 写锁可以原子地降级为读锁 | 可升级读、降级与 with_upgraded |
| 11 | 不用守卫也能直接解锁 | 外壳与守卫 |
| 12 | Mutex<()>、RwLock<()> 不用守卫也能直接加锁 | 同上 |
| 13 | 支持 eventual fairness,平均意义上公平而不牺牲性能 | eventual fairness |
| 14 | 可重入的 ReentrantMutex | ReentrantMutex |
| 15 | 实验性的死锁检测,覆盖 Mutex、RwLock、ReentrantMutex | 死锁检测 |
| 16 | 可升级读锁可以原子地升级为写锁 | 可升级读、降级与 with_upgraded |
| 17 | 可选的 serde 支持(只有 Mutex、ReentrantMutex、RwLock) | 外壳与守卫 |
| 18 | 开 send_guard 后守卫可以发往别的线程 | GuardSend 与 send_guard |
README 开头那句「在 x86_64 Linux 上无竞争快 1.5 倍、有竞争最多快 5 倍」是另一回事:它说的是某个时期、某个平台上和当时的 std 比;std 此后在 Linux 上换成了 futex 实现,在 macOS 上仍是 pthread。本机的对比在 和 std Mutex 比。
小结
- 三层:core 管停车场,lock_api 管外壳(
no_std,不依赖 core),parking_lot 把两边接起来;用户的Mutex<T>是lock_api::Mutex<RawMutex, T>。 - core 的心脏是
parking_lot.rs一个文件,约 1430 行非测试代码,其中死锁检测约 330 行。 - 本机 macOS 走
thread_parker/unix.rs(pthread),Linux 走linux.rs(futex)。 - parking_lot 的 feature 多数只是转发;
send_guard与deadlock_detection互斥,而且 feature 在全依赖图统一。 - README 的 18 条逐条对应到本卷的章,以代码和实测为准。