git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitac6e003a35ceb51c210e12e9ba4a49310dbea484
parentb363e5da43
authorIsaac Freund <[email protected]>
date2020-03-29 16:56
Handle timeout of transactions that take too long

 src/root.zig   | 36 ++++++++++++++++++++++++++++++++----
 src/server.zig |  7 ++++++-
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/src/root.zig b/src/root.zig
index 4246838..9598f6f 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -31,6 +31,9 @@ pub const Root = struct {
     // A value of 0 means there is no current transaction.
     pending_count: u32,
 
+    /// Handles timeout of transactions
+    transaction_timer: ?*c.wl_event_source,
+
     pub fn init(self: *Self, server: *Server) !void {
         self.server = server;
 
@@ -216,7 +219,27 @@ pub const Root = struct {
             }
         }
 
-        // TODO: start a timer and handle timeout waiting for all clients to ack
+        if (self.pending_count > 0) {
+            // TODO: log failure to create timer and commit immediately
+            self.transaction_timer = c.wl_event_loop_add_timer(
+                self.server.wl_event_loop,
+                handle_timeout,
+                self,
+            );
+            // Set timeout to 200ms
+            if (c.wl_event_source_timer_update(self.transaction_timer, 200) == -1) {
+                // TODO: handle failure
+            }
+        }
+    }
+
+    fn handle_timeout(data: ?*c_void) callconv(.C) c_int {
+        const root = @ptrCast(*Root, @alignCast(@alignOf(*Root), data));
+
+        // TODO: log warning
+        root.commitTransaction();
+
+        return 0;
     }
 
     pub fn notifyConfigured(self: *Self) void {
@@ -228,15 +251,20 @@ pub const Root = struct {
 
     /// Apply the pending state and drop stashed buffers. This means that
     /// the next frame drawn will be the post-transaction state of the
-    /// layout. Must only be called after all clients have configured for
-    /// the new layout.
+    /// layout. Should only be called after all clients have configured for
+    /// the new layout. If called early imperfect frames may be drawn.
     fn commitTransaction(self: *Self) void {
         // TODO: apply damage properly
+
+        // Ensure this is set to 0 to avoid entering invalid state (e.g. if called due to timeout)
+        self.pending_count = 0;
+
         var it = self.views.first;
         while (it) |node| : (it = node.next) {
             const view = &node.data;
 
-            // TODO: handle views that timed out
+            // Ensure that all pending state is cleared
+            view.pending_serial = null;
             view.current_state = view.pending_state;
             view.dropStashedBuffer();
         }
diff --git a/src/server.zig b/src/server.zig
index 266640f..c83c2d8 100644
--- a/src/server.zig
+++ b/src/server.zig
@@ -14,6 +14,7 @@ pub const Server = struct {
     allocator: *std.mem.Allocator,
 
     wl_display: *c.wl_display,
+    wl_event_loop: *c.wl_event_loop,
     wlr_backend: *c.wlr_backend,
     wlr_renderer: *c.wlr_renderer,
 
@@ -30,11 +31,15 @@ pub const Server = struct {
         self.allocator = allocator;
 
         // The Wayland display is managed by libwayland. It handles accepting
-        // clients from the Unix socket, manging Wayland globals, and so on.
+        // clients from the Unix socket, managing Wayland globals, and so on.
         self.wl_display = c.wl_display_create() orelse
             return error.CantCreateWlDisplay;
         errdefer c.wl_display_destroy(self.wl_display);
 
+        // Should never return null if the display was created successfully
+        self.wl_event_loop = c.wl_display_get_event_loop(self.wl_display) orelse
+            return error.CantGetEventLoop;
+
         // The wlr_backend abstracts the input/output hardware. Autocreate chooses
         // the best option based on the environment, for example DRM when run from
         // a tty or wayland if WAYLAND_DISPLAY is set.