Addressing global removal race in Wayland

⭐⭐⭐⭐ 4星 | 来源: Vlad Zahorodnii's Blog | 探索时间: 2026-03-25
Wayland 协议 系统编程 并发 竞争条件

摘要

深度解析Wayland协议中全局对象移除的竞争条件及解决方案。全球对象(Global objects)是Wayland的核心抽象,用于宣布支持的协议或半临时对象(如可用输出)。虽然添加新全局对象没有问题,但移除全局对象是一个竞争过程,处理不当会导致应用程序崩溃。

核心问题:当 compositor 在短时间内宣布并移除一个 wl_output 全局对象时,客户端可能在该对象已被移除后尝试绑定它,导致协议错误和客户端崩溃。

技术细节

竞争条件详解

  • Compositior 检测到新输出时,宣布相应的 wl_output 全局对象
  • 感兴趣的客户端绑定该 wl_output 并接收输出信息
  • 当移除 wl_output 时,理想情况下客户端应停止使用或绑定它
  • 问题:如果在全局对象移除的同时,客户端尝试绑定,会导致协议错误

第一次尝试:定时器方案

  • 思路:延迟销毁全局对象,给客户端时间处理移除事件
  • 问题:使用定时器销毁,但 Linux 睡眠时单调时钟会前进
  • 结果:计算机唤醒后定时器可能立即超时,仍有客户端未完成绑定
  • 结论:有所帮助但未能完全解决问题

第二次尝试:wl_fixes 接口

  • 引入 wl_fixes 接口绕过 wl_registry 冻结的限制
  • 协议变更:客户端需在收到 wl_registry.global_remove 事件后发送 ack 请求
  • Compositor 收到所有客户端的 ack 后才能安全销毁全局对象
  • 注意事项:所有客户端都必须正确响应,否则会积累"僵尸"全局对象

客户端修改

以下项目已添加对 wl_fixes.ack_global_remove 的支持:

Compositor 修改

  • libwayland-server 提供辅助函数
  • Compositor 实现 wl_fixes.ack_global_remove 请求
  • 调用 wl_global_remove() 设置 "withdrawn" 回调
  • libwayland-server 在安全时调用 withdrawn 回调
  • Compositor 然后调用 wl_global_destroy()

结论

虽然从一开始就可以用不同的方式宣布可用输出(不是作为全局对象),但 wl_output 确实暴露了 Wayland 核心协议中的一些缺陷,这些缺陷并不简单,并导致了各种客户端崩溃。通过本文描述的工作,希望 Wayland 会话变得更加可靠,更少的应用程序会意外崩溃。

← 返回索引