Rust in Everything
基于 lock_api 0.4.14

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&lt;R, T&gt;:raw + UnsafeCell&lt;T&gt;"]
    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&lt;()&gt;(自旋)"]
    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.rslock_api 本身
守卫什么时候解锁、能不能 map / unlocked / 跨线程同上,GuardMarker 决定 Sendlock_api + 实现者选的标记

四章:

  1. raw trait 与契约:RawMutex 的六个成员;为什么 trait 和 unlock 都是 unsafe;RawMutexFair / RawMutexTimed;RwLock 的 10 个 trait 一张表;GetThreadId;no_std 与 atomic_usize feature。
  2. 外壳与守卫:Mutex<R, T> 的两个字段;MutexGuard 只存一个引用;map 之后为什么不能 unlocked / bump;ArcMutexGuard;RwLock 的 8 种守卫;serde、#[clippy::has_significant_drop]。
  3. GuardSend 与 send_guard:GuardNoSend(*mut ()) 怎么让守卫变成 !Send;send_guard 只改一个类型别名;与 deadlock_detection 互斥;在 tokio 里持锁跨 .await 的编译错误和运行时卡死(E9)。
  4. 给自己的锁套外壳:文档里的自旋锁三步走,编译并测通;spin 0.9.9 的 lock_api feature 是同一种写法;外壳不能替你保证的东西。

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。两组都是编译期 / 正确性实验,不计时。

#目录看什么章
E9guard-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

💬 关联讨论

发起讨论
加载中...