在本站中
本站的代码里搜不到 parking_lot,所有 crate 的 Cargo.toml 里也没有它。Cargo.lock 里的 parking_lot 全是间接依赖,版本和本卷完全一致。这一页回答四个问题:谁把它带进来、开了哪些 feature、本站哪些代码路径会用到它、本卷的 09 常见坑 在本站碰不碰得到,最后看升级 0.13 的条件。
flowchart TB app["本站 app(server 构建)"] --> tokio["tokio 1.52.3,feature full 含 parking_lot"] app --> dsrv["dioxus-server 0.7.9:features = send_guard"] app --> dcore["dioxus-fullstack-core / dioxus-signals / generational-box 0.7.9"] app --> sqlx["sqlx-core 0.8.6"] sqlx --> fi["futures-intrusive 0.5.0"] tokio --> pl["parking_lot 0.12.5(default + send_guard)"] dsrv --> pl dcore --> pl fi --> pl gw["网关 crates/gateway(单独的 Cargo.lock)"] --> pg["pingora-core / cache / lru / pool / timeout 0.9.0"] pg --> fl["flurry 0.5.2(← pingora-core、pingora-lru)"] pg --> pl2["parking_lot 0.12.5(default + arc_lock)"] fl --> pl2 gw -.->|"tokio 1.52.3 没开 parking_lot"| tk2["tokio 内部用 std 锁"]
锁定的版本
写作时 main 的 3292b81:
| crate | 主工作区 Cargo.lock | 网关 crates/gateway/Cargo.lock | 本卷 |
|---|---|---|---|
| parking_lot | 0.12.5 | 0.12.5 | 0.12.5 |
| parking_lot_core | 0.9.12 | 0.9.12 | 0.9.12 |
| lock_api | 0.4.14 | 0.4.14 | 0.4.14 |
没有版本差异要讲。两个 lock 里都只有这一组版本,没有 0.13。
主工作区:谁带进来,开了什么
在本站目录下用 cargo tree --offline --locked -i parking_lot 反查(只解析 lock,不编译),三种口径:
| 口径 | 附加参数 | 直接依赖 parking_lot 的 crate | parking_lot 的 feature |
|---|---|---|---|
| 工作区默认 | -e normal --target all | tokio 1.52.3、dioxus-fullstack-core、dioxus-signals、generational-box(都是 0.7.9)、futures-intrusive 0.5.0 | default |
| 生产服务端 | -p app -F server --target x86_64-unknown-linux-gnu | 上面五个,加 dioxus-server 0.7.9 | default、send_guard |
| wasm 前端 | -p app -F web --target wasm32-unknown-unknown | dioxus-fullstack-core、dioxus-signals、generational-box | — |
| 桌面端 | -p app -F desktop --target aarch64-apple-darwin | 同工作区默认 | default |
parking_lot_core 只开 default,lock_api 开 atomic_usize(默认)。lock_api 另有一个直接用户 futures-intrusive(它自己的 GenericMutex<R: RawMutex> 等泛型锁)。
服务端构建打开了 send_guard
-F server 下多出来的那一条 feature 来自 dioxus-server 0.7.9 的 Cargo.toml:
[dependencies.parking_lot]
version = "0.12.4"
features = ["send_guard"]
它自己用 parking_lot 的地方是 src/document.rs 里一个 static RENDERER: LazyLock<RwLock<Renderer>>。按 GuardSend 与 send_guard 讲的 feature 合并规则,这意味着生产服务端进程里所有 parking_lot 守卫都是 Send,包括 tokio、dioxus 其他 crate 里的。两个直接后果:
- 本站服务端构建不能再给 parking_lot 打开
deadlock_detection,否则编译 parking_lot 时就是compile_error!(坑)。默认的 web 构建、桌面构建没有send_guard,可以开。 - tokio 不受影响:它内部把 parking_lot 的类型包成
struct Mutex<T>(PhantomData<std::sync::Mutex<T>>, parking_lot::Mutex<T>)一类的形状(tokio 1.52.3src/loom/std/parking_lot.rs第 12–28 行),注释写明是为了不让send_guard外溢到 tokio 自己类型的Send性上。
tokio 为什么开了 parking_lot
根 Cargo.toml 的 [workspace.dependencies] 写了 tokio = { version = "1.49.0", features = ["full"] },各 crate 用 workspace = true 继承;tokio 的 full 包含 parking_lot。dev 依赖里还有一条:llm crate 的 dev 依赖 mockito 1.7.2 默认 feature 含 parking_lot = ["tokio/parking_lot"]。
打开后 tokio 内部的同步锁(loom::sync::Mutex / RwLock / Condvar)全换成 parking_lot 的,切换点在 loom/std/mod.rs 第 55 行(tokio 卷 crate 图)。本站会走到的典型路径:
tokio::sync::Semaphore(batch_semaphore)的等待链表锁,只在改链表时持有(tokio 卷 batch-semaphore);tokio::sync::Mutex建在它上面。watch通道的值外面那把同步RwLock,borrow()持读锁(tokio 卷 watch)。- 运行时自身的调度、定时器、IO 驱动里的内部锁。
dioxus 与 sqlx
- generational-box 的
SyncStorage格子是parking_lot::RwLock,同线程拿着写锁再读就卡死(dioxus 卷 generational-box);dioxus-fullstack-core 的FullstackContext把请求头、上下文各放在一个Arc<RwLock<..>>里(src/streaming.rs)。 - futures-intrusive 0.5.0 由 sqlx-core 0.8.6 带进来,它的
std默认 feature 打开 parking_lot。但 sqlx-core 0.8.6 里 futures-intrusive 只出现在src/sync.rs的AsyncSemaphore,而且只在_rt-async-std且没开_rt-tokio时用;本站 sqlx 开的是_rt-tokio,那里用tokio::sync::Semaphore(sqlx 卷 PoolOptions 讲过fair只对 futures-intrusive 有意义)。所以 futures-intrusive 编进来了,本站运行时的连接池不经过它的 parking_lot 锁(读源码得出,没有在运行的进程里核对)。
网关工作区
crates/gateway 有自己的 Cargo.lock,同样是 0.12.5 一组:
| 依赖 parking_lot 的 crate | 用法(git grep registry 源码) |
|---|---|
| pingora-core 0.9.0 | 服务、监听、连接器里的 Mutex / RwLock;开了 arc_lock,connectors/http/v2.rs 第 471 行 release_lock.lock_arc() |
| pingora-cache 0.9.0 | 内存存储、哈希表、simple_lru 淘汰、cache lock(pingora 卷 存储与淘汰) |
| pingora-lru 0.9.0 | 分片 LRU,每片一把 RwLock(pingora 卷 TinyUFO 与内存缓存) |
| pingora-pool 0.9.0 | 连接池的 LRU 与连接表 |
| pingora-timeout 0.9.0 | 定时器表的 RwLock,fork 前要先暂停(pingora 卷 超时) |
| flurry 0.5.2(← pingora-core、pingora-lru) | 并发 HashMap 的每个桶一把 parking_lot::Mutex<()> |
parking_lot 的 feature 是 default + arc_lock(来自 pingora-core),lock_api 相应多了 arc_lock。网关的 tokio 1.52.3 没有开 parking_lot feature,tokio 内部用 std 锁;网关的部署见 pingora 卷 在本站中。
常见坑在本站
本站自己的代码不直接用 parking_lot,下面只看依赖的用法和构建配置:
| 坑 | 本站会不会碰到 |
|---|---|
| send_guard 与 deadlock_detection 冲突 | 会,只在服务端构建:dioxus-server 打开了 send_guard,想调试时给服务端开死锁检测会编译失败 |
| 跨 await 持守卫 | 服务端构建里守卫是 Send,编译器不会拦;本站代码不直接用 parking_lot,依赖里是否有持 parking_lot 守卫跨 .await 的写法,本页没有逐个核对 |
| 同线程重入、写者排队时重复读 | 经依赖碰得到:SyncStorage 同线程写后读(dioxus 卷已有描述)、watch::borrow() 持读锁时在同一线程再读同一个 watch |
| 插队饿死 | 0.12.5 有约 1 ms 的兜底;升级 0.13 后才可能显出来,本站没有长临界区循环的已知用法 |
| 无毒化 | 依赖内部的锁在 panic 后照常可用。tokio 的包装层 lock() 直接返回守卫,Condvar::wait 系列恒返回 Ok(src/loom/std/parking_lot.rs 第 61、160–172 行);这只是 tokio 内部的接口,本站代码看不到 |
| Condvar 相关三条、Once 毒化 | 本站与上面列出的依赖都没有直接用 parking_lot 的 Condvar / Once(tokio 的 Condvar 包装在打开 feature 时是 parking_lot 的,用在 runtime/park.rs、runtime/blocking/pool.rs 等内部路径上) |
| 死锁检测盲区、墙上时钟 | 不涉及:没有开死锁检测;生产服务器是 Linux,墙上时钟问题只在 Apple 平台 |
升级到 0.13 要等什么
本站不改依赖,这里只看 registry 里各上游的版本约束(Cargo.toml 的 [dependencies.parking_lot]):
| crate(本站版本 → registry 里最新) | 对 parking_lot 的要求 | 能用 0.13 吗 |
|---|---|---|
| tokio 1.52.3 → 1.54.1 | 0.12.0(optional) | 不能;而且 tokio 的包装里调用 parking_lot::const_mutex,0.13 已删除这个函数 |
| dioxus-signals / generational-box / dioxus-fullstack-core / dioxus-server 0.7.9 → 0.8.0-alpha.1 | 0.12.4 | 不能 |
| futures-intrusive 0.5.0(sqlx-core 0.8.6 与 0.9.0 都依赖它) | parking_lot 0.12.0,lock_api 0.4.1 | 不能;lock_api 0.5 的 trait 签名也变了 |
| pingora-core / cache / pool / timeout 0.9.0、flurry 0.5.2 | 0.12 | 不能 |
| pingora-lru 0.9.0 | 0(即 >=0.0.0, <1.0.0) | 按 semver 允许 0.13;它只用 parking_lot::RwLock |
结论:本站两个工作区里几乎所有上游都把 parking_lot 钉在 0.12 系列,tokio 还用到了 0.13 删掉的 API。升级要等这些上游发版。0.12 和 0.13 是不兼容的两个版本,Cargo 可以让它们并存:将来若只有一部分上游升级,进程里会同时有两份 parking_lot、两份 parking_lot_core(各自一张全局哈希表),这是 0.13 的「各依赖各自升级」阶段的正常现象。pingora-lru 的 0 约束在 cargo update 时会不会被解析到 0.13.0,本页没有试(不改本站的 lock)。升级时要逐条对照的行为差别见 07 0.13 的变化,最可能影响本站的是去掉自动的最终公平(去掉的与修正的)。
小结
- 本站没有直接依赖 parking_lot;主工作区和网关都锁定 0.12.5 / 0.9.12 / 0.4.14,与本卷相同。
- 主工作区经 tokio(
full含parking_lot)、dioxus 0.7.9 一族、futures-intrusive(← sqlx-core)带进来;网关经 pingora 0.9.0 和 flurry,网关的 tokio 没开parking_lot。 - 生产服务端构建里 dioxus-server 打开了
send_guard:所有 parking_lot 守卫都是Send,服务端不能再开deadlock_detection;tokio 用PhantomData挡住了外溢。网关开了arc_lock。 - sqlx 在 tokio 运行时下用 tokio 的信号量,futures-intrusive 编进来但连接池不走它的锁。
- 上游几乎都要求 0.12,tokio 还用了 0.13 删除的
const_mutex;本站升级 0.13 要等上游。