termio/exec: avoid potential deadlock with surface message

Fixes #1198

This adds a fix similar to what we discovered with termio messages: we
attempt to send a surface message but if the queue is full we unlock the
terminal state and try again waiting forever.

In all cases, its safe to unlock the mutex while sending the message, no
scenario we send a surface message requires this lock to be held.
This commit is contained in:
Mitchell Hashimoto
2024-01-02 12:35:31 -08:00
parent 143de07aaa
commit 797da2f737

View File

@ -1695,6 +1695,19 @@ const StreamHandler = struct {
try self.ev.queueRender(); try self.ev.queueRender();
} }
inline fn surfaceMessageWriter(
self: *StreamHandler,
msg: apprt.surface.Message,
) void {
// See messageWriter which has similar logic and explains why
// we may have to do this.
if (self.ev.surface_mailbox.push(msg, .{ .instant = {} }) == 0) {
self.ev.renderer_state.mutex.unlock();
defer self.ev.renderer_state.mutex.lock();
_ = self.ev.surface_mailbox.push(msg, .{ .forever = {} });
}
}
inline fn messageWriter(self: *StreamHandler, msg: termio.Message) void { inline fn messageWriter(self: *StreamHandler, msg: termio.Message) void {
// Try to write to the mailbox with an instant timeout. This is the // Try to write to the mailbox with an instant timeout. This is the
// fast path because we can queue without a lock. // fast path because we can queue without a lock.
@ -2487,10 +2500,7 @@ const StreamHandler = struct {
// Mark that we've seen a title // Mark that we've seen a title
self.ev.seen_title = true; self.ev.seen_title = true;
self.surfaceMessageWriter(.{ .set_title = buf });
_ = self.ev.surface_mailbox.push(.{
.set_title = buf,
}, .{ .forever = {} });
} }
pub fn setMouseShape( pub fn setMouseShape(
@ -2502,9 +2512,7 @@ const StreamHandler = struct {
if (self.terminal.mouse_shape == shape) return; if (self.terminal.mouse_shape == shape) return;
self.terminal.mouse_shape = shape; self.terminal.mouse_shape = shape;
_ = self.ev.surface_mailbox.push(.{ self.surfaceMessageWriter(.{ .set_mouse_shape = shape });
.set_mouse_shape = shape,
}, .{ .forever = {} });
} }
pub fn clipboardContents(self: *StreamHandler, kind: u8, data: []const u8) !void { pub fn clipboardContents(self: *StreamHandler, kind: u8, data: []const u8) !void {
@ -2521,14 +2529,12 @@ const StreamHandler = struct {
// Get clipboard contents // Get clipboard contents
if (data.len == 1 and data[0] == '?') { if (data.len == 1 and data[0] == '?') {
_ = self.ev.surface_mailbox.push(.{ self.surfaceMessageWriter(.{ .clipboard_read = clipboard_type });
.clipboard_read = clipboard_type,
}, .{ .forever = {} });
return; return;
} }
// Write clipboard contents // Write clipboard contents
_ = self.ev.surface_mailbox.push(.{ self.surfaceMessageWriter(.{
.clipboard_write = .{ .clipboard_write = .{
.req = try apprt.surface.Message.WriteReq.init( .req = try apprt.surface.Message.WriteReq.init(
self.alloc, self.alloc,
@ -2536,7 +2542,7 @@ const StreamHandler = struct {
), ),
.clipboard_type = clipboard_type, .clipboard_type = clipboard_type,
}, },
}, .{ .forever = {} }); });
} }
pub fn promptStart(self: *StreamHandler, aid: ?[]const u8, redraw: bool) !void { pub fn promptStart(self: *StreamHandler, aid: ?[]const u8, redraw: bool) !void {
@ -2808,6 +2814,6 @@ const StreamHandler = struct {
@memcpy(message.desktop_notification.body[0..body_len], body[0..body_len]); @memcpy(message.desktop_notification.body[0..body_len], body[0..body_len]);
message.desktop_notification.body[body_len] = 0; message.desktop_notification.body[body_len] = 0;
_ = self.ev.surface_mailbox.push(message, .{ .forever = {} }); self.surfaceMessageWriter(message);
} }
}; };