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).
This commit is contained in:
Mitchell Hashimoto
2023-11-10 22:01:07 -08:00
parent ace5693957
commit bde9b02db3
2 changed files with 18 additions and 2 deletions

View File

@ -60,7 +60,9 @@ pub fn Stream(comptime Handler: type) type {
for (actions) |action_opt| {
const action = action_opt orelse continue;
// if (action != .print) {
// log.info("action: {}", .{action});
// }
// If this handler handles everything manually then we do nothing
// if it can be processed.

View File

@ -1398,7 +1398,21 @@ const StreamHandler = struct {
}
inline fn messageWriter(self: *StreamHandler, msg: termio.Message) void {
// 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;
}