Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
WindowManager: disconnect wm if unresponsive
Currently a window manager entering an infinite loop and not
responding to a manage/render sequence causes river to lock up.
With this patch, river will now disconnect a window manager if it takes
more than 3 seconds to respond to a manage/render sequence.
Ideally, river would automatically start a fallback window manager in
this case, but we don't have one yet.
protocol/river-window-management-v1.xml | 2 ++
river/WindowManager.zig | 32 ++++++++++++++++++++++++--------
2 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml
index cdb273e..db170d4 100644
--- a/protocol/river-window-management-v1.xml
+++ b/protocol/river-window-management-v1.xml
@@ -120,6 +120,8 @@
summary="request violates manage/render sequence ordering"/>
<entry name="role" value="1"
summary="given wl_surface already has a role"/>
+ <entry name="unresponsive" value="2"
+ summary="window manager unresponsive"/>
</enum>
<event name="unavailable">
diff --git a/river/WindowManager.zig b/river/WindowManager.zig
index 8d39e60..c6025ef 100644
--- a/river/WindowManager.zig
+++ b/river/WindowManager.zig
@@ -310,6 +310,7 @@ fn manageStart(wm: *WindowManager) void {
if (wm.object) |wm_v1| {
wm_v1.sendManageStart();
+ wm.startTimeoutTimer(3000);
} else {
wm.manageFinish();
}
@@ -317,6 +318,7 @@ fn manageStart(wm: *WindowManager) void {
pub fn manageFinish(wm: *WindowManager) void {
assert(wm.state == .manage);
+ wm.cancelTimeoutTimer();
log.debug("manage sequence finish", .{});
@@ -345,14 +347,14 @@ pub fn manageFinish(wm: *WindowManager) void {
log.debug("sent {} tracked configure(s)", .{wm.state.inflight_configures});
if (wm.state.inflight_configures > 0) {
- wm.startTimeoutTimer();
+ wm.startTimeoutTimer(100);
} else {
wm.renderStart();
}
}
-fn startTimeoutTimer(wm: *WindowManager) void {
- wm.timeout.timerUpdate(100) catch {
+fn startTimeoutTimer(wm: *WindowManager, ms: u31) void {
+ wm.timeout.timerUpdate(ms) catch {
log.err("failed to start timer", .{});
_ = wm.handleTimeout();
};
@@ -363,12 +365,23 @@ fn cancelTimeoutTimer(wm: *WindowManager) void {
}
fn handleTimeout(wm: *WindowManager) c_int {
- log.err("timeout occurred, some imperfect frames may be shown", .{});
-
- assert(wm.state.inflight_configures > 0);
- wm.state.inflight_configures = 0;
+ switch (wm.state) {
+ .inflight_configures => {
+ log.err("timeout occurred, some imperfect frames may be shown", .{});
+ assert(wm.state.inflight_configures > 0);
+ wm.state.inflight_configures = 0;
- wm.renderStart();
+ wm.renderStart();
+ },
+ .manage, .render => {
+ log.err("window manager unresponsive for more than 5 seconds, disconnecting", .{});
+ wm.object.?.postError(.unresponsive, "unresponsive for more than 5 seconds");
+ // Don't wait for the frozen client to receive the protocol error
+ // and exit of its own accord.
+ wm.object.?.getClient().destroy();
+ },
+ .idle => unreachable,
+ }
return 0;
}
@@ -401,6 +414,7 @@ fn renderStart(wm: *WindowManager) void {
if (wm.object) |wm_v1| {
wm_v1.sendRenderStart();
+ wm.startTimeoutTimer(3000);
} else {
wm.renderFinish();
}
@@ -412,6 +426,8 @@ fn renderFinish(wm: *WindowManager) void {
assert(wm.state == .render);
wm.state = .idle;
+ wm.cancelTimeoutTimer();
+
log.debug("render sequence finish", .{});
{