Rust in Everything
基于 parking_lot 0.12.5

源码地图

主线位置:四条主线经过的 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_core0.9.12cfg-if、smallvec;unix 上 libc,Windows 上 windows-link,redox 上 redox_syscall;可选 petgraph、backtrace3687,其中 parking_lot.rs 第 1429 行起约 270 行是测试02
lock_api0.4.14scopeguard;可选 owning_ref、serde5425,大部分是文档注释06
parking_lot0.12.5只有 parking_lot_core 和 lock_api5216,其中 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.rs67crate 文档(停车场的来历)、导出 park / unpark_* / SpinWait / deadlockwhy-parking-lot
parking_lot.rs1700全局哈希表与扩容、ThreadData、FairTimeout、park 与四个 unpark 函数、ParkToken / UnparkToken / RequeueOp / FilterOp,死锁检测(1095–1427),测试(1429 起)hashtable、park-protocol、eventual-fairness、deadlock-detection
word_lock.rs327桶锁 WordLock:一个 usize 里两位标志加一个队列头指针,自己管等待队列word-lock
spinwait.rs74SpinWait:10 步,前 3 步自旋、后 7 步让出spinwait
util.rs31unchecked_unwrap:release 下是 unreachable_unchecked—
thread_parker/mod.rs85ThreadParkerT / UnparkHandleT 两个 trait,cfg_if 按平台选实现thread-parker
thread_parker/unix.rs242非 Linux 的 unix(本机 macOS 走这里):pthread_mutex_t + pthread_cond_t + 一个 Cell<bool>同上
thread_parker/linux.rs156Linux / 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.rs139、109、97、54、79其他平台;wasm.rs 一停车就 panic,generic.rs 是忙等同上(一张表)

parking_lot:每个文件一句话

文件行数管什么本卷
lib.rs62导出;send_guard 与 deadlock_detection 互斥的 compile_error!;GuardMarker 类型别名guard-send
raw_mutex.rs331RawMutex:LOCKED_BIT / PARKED_BIT 两位、lock_slow / unlock_slow / bump_slow,TOKEN_NORMAL / TOKEN_HANDOFFraw-mutex
mutex.rs385Mutex<T> / MutexGuard 类型别名、const_mutex,其余是文档和测试同上
raw_fair_mutex.rs65RawFairMutex(RawMutex):unlock 转 unlock_fairfair-and-bump
fair_mutex.rs274FairMutex<T> 类型别名,其余是文档和测试同上
remutex.rs171RawThreadId:线程局部变量的地址当线程 id;ReentrantMutex<T> 别名reentrant
raw_rwlock.rs1158RawRwLock:状态字、主队列与 addr + 1 第二队列、读 / 写 / 可升级三种 token、wake_parked_threads04 RwLock
rwlock.rs659RwLock<T> 与各守卫的类型别名,其余是文档和测试同上
elision.rs119x86 TSX 的 xacquire / xrelease,只在 hardware-lock-elision feature 下用state-word
condvar.rs1270Condvar{state: AtomicPtr<RawMutex>}、notify_* 的 requeue、wait*condvar
once.rs4521 字节的 Once:DONE / POISON / LOCKED / PARKED 四位once
deadlock.rs232重新导出 check_deadlock,其余是测试deadlock-detection
util.rs38to_deadline:Duration 转 Instant,溢出时返回 None—

lock_api:每个文件一句话

文件行数管什么本卷
lib.rs117crate 文档(自旋锁示例)、GuardSend / GuardNoSend、atomic_usize 控制 remutex 是否编译traits、guard-send
mutex.rs1039RawMutex / RawMutexFair / RawMutexTimed,Mutex<R, T>,MutexGuard、MappedMutexGuard、ArcMutexGuardtraits、wrappers
remutex.rs1139GetThreadId、RawReentrantMutex(计数 + 所有者)、ReentrantMutex 与守卫reentrant
rwlock.rs3130RwLock 的 9 个 raw trait,RwLock<R, T>,读 / 写 / 可升级三类守卫 × 普通 / Mapped / Arcwrappers、upgradable

feature 转发表

parking_lot 自己的 feature 大多只是转发(根 Cargo.toml):

parking_lot 的 feature转发到作用本卷
arc_locklock_api/arc_locklock_arc() 返回持有 Arc 的守卫wrappers
owning_reflock_api/owning_ref给守卫实现 owning_ref 的 trait(这个依赖已不维护,一句带过)同上
serdelock_api/serde序列化锁里的数据,反序列化时新建一把锁同上
nightlyparking_lot_core/nightly、lock_api/nightlycore 里只影响 wasm 原子等待后端的选择(thread_parker/mod.rs:69-75);lock_api 的 nightly 在 src/ 里没有任何引用thread-parker
deadlock_detectionparking_lot_core/deadlock_detection打开死锁检测,拉进 petgraph、backtracedeadlock-detection
send_guard本 crate把 GuardMarker 从 GuardNoSend 换成 GuardSend(src/lib.rs:32-39)guard-send
hardware-lock-elision本 cratex86 上 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.rs311线程数、临界区内工作量、临界区外工作量、每轮秒数、轮数parking_lot::Mutex、std::sync::Mutex,Windows 上加 SRWLOCK,其他平台加裸 pthread_mutex_t
benchmark/src/bin/rwlock.rs419写者数、读者数,其余同上parking_lot::RwLock、std::sync::RwLock、seqlock::SeqLock,Windows 上加 SRWLOCK,其他平台加 pthread_rwlock_t

本卷自己的吞吐实验(E2)沿用同样的思路:临界区外的工作量是决定「谁快」的关键参数,见 和 std Mutex 比。

tests/ 只有两个回归测试,都是 RwLock:

文件行数测什么
tests/issue_203.rs26在线程局部变量的析构函数里对另一个线程局部的 RwLock 加锁(TLS 析构期间停车场还要能用;对应 with_thread_data 在 TLS 已析构时退回栈上临时 ThreadData 的分支,见 停车场哈希表)
tests/issue_392.rs15同一个可升级读守卫连续调两次 with_upgraded(64b7114「Fix RwLockUpgradeableReadGuard::with_upgraded」)

README 的 18 条:在哪章核实

README 第 27–72 行列了 18 条「优于标准库」的地方。它是资料,不是结论;本卷逐条对照代码或实测:

#README 的说法(大意)本卷
1Mutex、Once 只要 1 字节,Condvar、RwLock 只要 1 个字;std 在 macOS 等平台要堆分配和 std Mutex 比(E1)
2无竞争的加锁与解锁走内联快路径,只要一次原子操作RawMutex 的一字节状态机
3短临界区的「微竞争」靠自旋几次处理SpinWait
4自旋几次失败后挂起线程,长短临界区都适用同上
5Condvar、RwLock、Once 能在 Windows XP 上用ThreadParker 的各平台实现
6RwLock 可用硬件锁省略(hardware-lock-elision)状态字与两条队列
7RwLock 是 task-fair 的,读者写者都不饿死读、写与写者优先
8Condvar 保证没有虚假唤醒Condvar
9notify_all 只叫醒一个,其余 requeue 到 mutex 上,避免惊群一次 notify_all、Condvar
10写锁可以原子地降级为读锁可升级读、降级与 with_upgraded
11不用守卫也能直接解锁外壳与守卫
12Mutex<()>、RwLock<()> 不用守卫也能直接加锁同上
13支持 eventual fairness,平均意义上公平而不牺牲性能eventual fairness
14可重入的 ReentrantMutexReentrantMutex
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 条逐条对应到本卷的章,以代码和实测为准。

💬 关联讨论

发起讨论
加载中...