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 的
RwLock和Mutex也使用 SRW;Mutex是安全的,因为它只使用独占模式。 - WINE 和 ReactOS 使用基于 CAS 的设计,看起来没有这个 bug。
- 有人分享了自己实现 SRW/pushlock 变体的经验,强调即使数据结构很小,实现也依然复杂。
Bug 报告与支持体验
- 反馈 Windows API bug 被描述为非常困难;Feedback Hub 被认为信噪比很低。
- 可行的绕路包括:提交“文档 bug”、使用 GitHub issue tracker(针对 STL)、或通过社交媒体联系工程师。
- 对比之下,Linux kernel 和一些 Apple 团队被描述为响应更积极,而大型厂商通常更优先考虑路线图和付费客户。
- 对企业反馈系统的普遍挫败感:低质量报告、第一线支持脚本,以及在 bug tracker 里“对着虚空呐喊”。