Windows API 中读写锁的 Bug

Windows 的 slim reader/writer(SRW)锁中发现了一个长期存在的 bug,借由 C++ 的 `std::shared_mutex` 被暴露出来:某个请求共享锁的线程有时会意外获得独占访问,并可能在某些模式下导致死锁。评论者指出,这种行为很可能早在 Vista 时代就已存在,源于并发原语中细微的实现选择与公平性权衡;而 Wine 和 ReactOS 中的替代实现似乎不受影响。这一事件也凸显出外部开发者通过官方渠道报告底层 Windows bug 的困难程度;与更开放或维护更好的生态相比,许多人因此在性能分析证明值得之前,都会尽量避免使用读写锁。

Windows SRWLOCK bug 和影响

  • Bug 出在 Windows 的 slim reader/writer(SRW)锁上;Windows 上的 std::shared_mutex 是建立在 SRW 之上的,因此也继承了这个问题。
  • 场景:在一个独占持有者释放锁时,多个读者正试图同时获取共享访问,锁可能会死锁。
  • 一位 Microsoft 工程师确认已经提交了一个内部 OS bug;目前争论点在于这是实现 bug,还是契约规定得不够明确。
  • 低层分析给出的解释是:独占释放时的非原子序列,可能让某个共享获取者最终拿到的是 exclusive 锁,或者以其他方式“抢走”锁,导致等待者没有被唤醒。

读写锁的语义与设计

  • 多位评论者指出,RW 锁看似简单,实际上非常复杂,而且很容易出现微妙的 bug;除非性能分析证明有收益,否则有人会避免使用它们。
  • 共享模式被视为一种优化,而不是严格保证所有读者都能始终共存。
  • 公平性与性能:SRW 锁刻意设计得不公平,以避免写者饥饿并减少上下文切换开销;这会在已有读者活动时,阻塞后到达的读者。
  • 讨论中提到的替代模式包括:双缓冲、更细粒度的锁、类似 RCU 的方案、基于 shared_ptr 的版本管理。

重现程序的正确性

  • 一位评论者声称示例里对一个非原子的线程计数存在数据竞争,认为单这一点就可能导致挂起。
  • 其他人反驳:线程创建/加入建立了 happens-before 关系;在 x86 上并且使用 std::thread 时,这种模式被认为是安全的,而把计数改成 constexpr 或原子变量也不能消除这个 bug。
  • 线程中的共识更倾向于“程序本身没问题;是 SRW 实现有错。”

替代实现与平台

  • Windows 上 Rust 的 RwLockMutex 也使用 SRW;Mutex 是安全的,因为它只使用独占模式。
  • WINE 和 ReactOS 使用基于 CAS 的设计,看起来没有这个 bug。
  • 有人分享了自己实现 SRW/pushlock 变体的经验,强调即使数据结构很小,实现也依然复杂。

Bug 报告与支持体验

  • 反馈 Windows API bug 被描述为非常困难;Feedback Hub 被认为信噪比很低。
  • 可行的绕路包括:提交“文档 bug”、使用 GitHub issue tracker(针对 STL)、或通过社交媒体联系工程师。
  • 对比之下,Linux kernel 和一些 Apple 团队被描述为响应更积极,而大型厂商通常更优先考虑路线图和付费客户。
  • 对企业反馈系统的普遍挫败感:低质量报告、第一线支持脚本,以及在 bug tracker 里“对着虚空呐喊”。