Rust in Everything
基于 parking_lot 0.12.5

the send_guard and deadlock_detection features cannot be used together

主线位置:不在主线上,是构建期的问题。两个 feature 一个改守卫的 Send 性,一个按加锁线程记账,parking_lot 选择让它们互斥(GuardSend 与 send_guard);Cargo 的 feature 合并让「互斥」变成了「依赖图里任何两处不能分别打开」。

flowchart LR
  a["crate A:parking_lot features = send_guard"] --> u["Cargo 合并 feature:同一个 parking_lot v0.12.5"]
  b["crate B:parking_lot features = deadlock_detection"] --> u
  u --> ce["编译 parking_lot:compile_error!"]
  ce --> find["cargo tree -e features -i parking_lot"]

报错原文

E9 ① 的第三种组合(exp-parking_lot/guard-await,--features sg,dd,registry 路径已缩短):

    Checking parking_lot v0.12.5
error: the `send_guard` and `deadlock_detection` features cannot be used together
  --> parking_lot-0.12.5/src/lib.rs:35:1
   |
35 | compile_error!("the `send_guard` and `deadlock_detection` features cannot be used together");
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: could not compile `parking_lot` (lib) due to 1 previous error

错误出现在编译 parking_lot 的时候,指向的是 registry 里的源码,不是你的代码。你自己的 Cargo.toml 可能一个 feature 都没开。

源码出处

src/lib.rs:32-39:

// If deadlock detection is enabled, we cannot allow lock guards to be sent to
// other threads.
#[cfg(all(feature = "send_guard", feature = "deadlock_detection"))]
compile_error!("the `send_guard` and `deadlock_detection` features cannot be used together");

死锁检测把「本线程持有哪些锁」记在加锁线程的 ThreadData 里,解锁时在当前线程的记录里删(死锁检测)。守卫换了线程,记录就对不上,上游因此禁止两者同开。send_guard 引入于 0.11.0(#240,提交说明「But only if deadlock detection is not enabled.」)。

Cargo 对同一个包取所有依赖打开的 feature 的并集。所以:

  • A 依赖开了 send_guard,B 依赖(或者你为了调试)开了 deadlock_detection,两者在 parking_lot 里相遇。
  • 反过来,只有一方开了 send_guard,整个程序里所有 parking_lot 守卫都变成 Send,包括你自己代码里的,编译器不会提醒。

怎么找出是谁

cargo tree -e features -i parking_lot

guard-await 开 sg 时的输出(节选,完整输出在 GuardSend 与 send_guard),最后一个分支就是打开 send_guard 的人:

parking_lot v0.12.5
├── parking_lot feature "arc_lock"
│   └── guard-await v0.1.0 (exp-parking_lot/guard-await)
├── parking_lot feature "default"
│   └── guard-await v0.1.0 (exp-parking_lot/guard-await) (*)
└── parking_lot feature "send_guard"
    └── guard-await feature "sg" (command-line)

本站两个工作区的 parking_lot 都没有这两个 feature,见 10 在本站中。

怎么改

  • 调试时临时打开 deadlock_detection 撞上了这个错误:说明依赖图里有人开了 send_guard,调试期间只能在不依赖它的二进制或测试里打开死锁检测。
  • 库作者不要在库里打开 send_guard:它会改变所有下游的守卫类型,还会让下游无法开死锁检测。库里需要可跨线程的守卫,用 ArcMutexGuard(arc_lock feature)或换一种锁。
  • 库作者也可以学 tokio 把外溢挡住:tokio 内部把 parking_lot 的类型再包一层,每个包装都带 PhantomData<std::sync::...>,注释写明是为了「ensure that parking_lot's send_guard feature does not leak through and affect when Tokio types are Send」(tokio 1.52.3 src/loom/std/parking_lot.rs 第 12–15 行,tokio PR #4359)。这样别人开不开 send_guard,tokio 自己类型的 Send 性都和用 std 锁时一样。

小结

  • 报错来自 src/lib.rs:35 的 compile_error!,出现在编译 parking_lot 时。
  • 原因通常是依赖图里两处分别打开了两个 feature,Cargo 合并后撞在一起。
  • cargo tree -e features -i parking_lot 的分支直接写出是谁打开了哪个 feature。
  • 库不要打开 send_guard;需要挡住外溢可以学 tokio 用 PhantomData 包一层。

💬 关联讨论

发起讨论
加载中...