summaryrefslogtreecommitdiff
path: root/src/termio/Thread.zig
diff options
context:
space:
mode:
authorMitchell Hashimoto <mitchell.hashimoto@gmail.com>2023-08-28 11:35:40 -0700
committerMitchell Hashimoto <mitchell.hashimoto@gmail.com>2023-08-28 11:35:40 -0700
commit2cc1e4371651ccd692f3e8e8ba5a5cf731b2e21f (patch)
treed7d00270b650ebb7eebeb064aca746f89e608a12 /src/termio/Thread.zig
parent3b9d5d27adce1f48b14ffb329c00ae1fe4bee38a (diff)
termio: handle all the synchronized output setting, timer
Diffstat (limited to 'src/termio/Thread.zig')
-rw-r--r--src/termio/Thread.zig48
1 files changed, 48 insertions, 0 deletions
diff --git a/src/termio/Thread.zig b/src/termio/Thread.zig
index 2c32d459f..6f2a4af3f 100644
--- a/src/termio/Thread.zig
+++ b/src/termio/Thread.zig
@@ -27,6 +27,10 @@ const Coalesce = struct {
resize: ?termio.Message.Resize = null,
};
+/// The number of milliseconds before we reset the synchronized output flag
+/// if the running program hasn't already.
+const sync_reset_ms = 5000;
+
/// Allocator used for some state
alloc: std.mem.Allocator,
@@ -49,6 +53,12 @@ coalesce_c: xev.Completion = .{},
coalesce_cancel_c: xev.Completion = .{},
coalesce_data: Coalesce = .{},
+/// This timer is used to reset synchronized output modes so that
+/// the terminal doesn't freeze with a bad actor.
+sync_reset: xev.Timer,
+sync_reset_c: xev.Completion = .{},
+sync_reset_cancel_c: xev.Completion = .{},
+
/// The underlying IO implementation.
impl: *termio.Impl,
@@ -79,6 +89,10 @@ pub fn init(
var coalesce_h = try xev.Timer.init();
errdefer coalesce_h.deinit();
+ // This timer is used to reset synchronized output modes.
+ var sync_reset_h = try xev.Timer.init();
+ errdefer sync_reset_h.deinit();
+
// The mailbox for messaging this thread
var mailbox = try Mailbox.create(alloc);
errdefer mailbox.destroy(alloc);
@@ -89,6 +103,7 @@ pub fn init(
.wakeup = wakeup_h,
.stop = stop_h,
.coalesce = coalesce_h,
+ .sync_reset = sync_reset_h,
.impl = impl,
.mailbox = mailbox,
};
@@ -98,6 +113,7 @@ pub fn init(
/// completes executing; the caller must join prior to this.
pub fn deinit(self: *Thread) void {
self.coalesce.deinit();
+ self.sync_reset.deinit();
self.stop.deinit();
self.wakeup.deinit();
self.loop.deinit();
@@ -158,6 +174,7 @@ fn drainMailbox(self: *Thread) !void {
.clear_screen => |v| try self.impl.clearScreen(v.history),
.scroll_viewport => |v| try self.impl.scrollViewport(v),
.jump_to_prompt => |v| try self.impl.jumpToPrompt(v),
+ .start_synchronized_output => self.startSynchronizedOutput(),
.write_small => |v| try self.impl.queueWrite(v.data[0..v.len]),
.write_stable => |v| try self.impl.queueWrite(v),
.write_alloc => |v| {
@@ -174,6 +191,18 @@ fn drainMailbox(self: *Thread) !void {
}
}
+fn startSynchronizedOutput(self: *Thread) void {
+ self.sync_reset.reset(
+ &self.loop,
+ &self.sync_reset_c,
+ &self.sync_reset_cancel_c,
+ sync_reset_ms,
+ Thread,
+ self,
+ syncResetCallback,
+ );
+}
+
fn handleResize(self: *Thread, resize: termio.Message.Resize) void {
self.coalesce_data.resize = resize;
@@ -193,6 +222,25 @@ fn handleResize(self: *Thread, resize: termio.Message.Resize) void {
);
}
+fn syncResetCallback(
+ self_: ?*Thread,
+ _: *xev.Loop,
+ _: *xev.Completion,
+ r: xev.Timer.RunError!void,
+) xev.CallbackAction {
+ _ = r catch |err| switch (err) {
+ error.Canceled => {},
+ else => {
+ log.warn("error during sync reset callback err={}", .{err});
+ return .disarm;
+ },
+ };
+
+ const self = self_ orelse return .disarm;
+ self.impl.resetSynchronizedOutput();
+ return .disarm;
+}
+
fn coalesceCallback(
self_: ?*Thread,
_: *xev.Loop,