From bde9b02db389e056dd790dab45d20ff6a8d24b09 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 10 Nov 2023 22:01:07 -0800 Subject: [PATCH] termio: wake up writer thread if the writer mailbox is full Normally, we queue all the writes we need from a single `read()` syscall and only wake up the writer thread at the end of processing that batch of data. But under very heavy load or large batches of data, it is possible for the terminal sequences to generate enough writes to the pty to fill the writer thread queue while we're still processing the data from `read()`. This modifies our queuer to attempt to queue, but if the queue is full we wake up the writer thread immediately then queue again (which should succeed in every case -- eventually). --- src/terminal/stream.zig | 4 +++- src/termio/Exec.zig | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index e41b27849..c438776dd 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -60,7 +60,9 @@ pub fn Stream(comptime Handler: type) type { for (actions) |action_opt| { const action = action_opt orelse continue; - // log.info("action: {}", .{action}); + // if (action != .print) { + // log.info("action: {}", .{action}); + // } // If this handler handles everything manually then we do nothing // if it can be processed. diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 7a409e8f7..a4a3e4eb1 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -1398,7 +1398,21 @@ const StreamHandler = struct { } inline fn messageWriter(self: *StreamHandler, msg: termio.Message) void { - _ = self.ev.writer_mailbox.push(msg, .{ .forever = {} }); + // Try to write to the mailbox with an instant timeout. If the + // mailbox is full, then we wake up the writer thread mid-processing + // so it can process the message and then try again with a forever + // wait. + if (self.ev.writer_mailbox.push(msg, .{ .instant = {} }) == 0) { + self.ev.writer_wakeup.notify() catch |err| { + log.warn("failed to wake up writer, data will be dropped err={}", .{err}); + return; + }; + + _ = self.ev.writer_mailbox.push(msg, .{ .forever = {} }); + } + + // Normally, we just flag this true to wake up the writer thread + // once per batch of data. self.writer_messaged = true; }