Addressing global removal race in 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 的支持:
- Qt: Code Review
- GTK3: Merge Request
- GTK4: Merge Request
- SDL: PR #15203
- Xwayland: Merge Request
- Mesa: Merge Request
- Chromium: Review
- Firefox: Phabricator
- wayland-rs: PR #887
Compositor 修改
- libwayland-server 提供辅助函数
- Compositor 实现 wl_fixes.ack_global_remove 请求
- 调用 wl_global_remove() 设置 "withdrawn" 回调
- libwayland-server 在安全时调用 withdrawn 回调
- Compositor 然后调用 wl_global_destroy()
结论
虽然从一开始就可以用不同的方式宣布可用输出(不是作为全局对象),但 wl_output 确实暴露了 Wayland 核心协议中的一些缺陷,这些缺陷并不简单,并导致了各种客户端崩溃。通过本文描述的工作,希望 Wayland 会话变得更加可靠,更少的应用程序会意外崩溃。
← 返回索引