summaryrefslogtreecommitdiff
path: root/src/termio/Thread.zig
diff options
context:
space:
mode:
authorMitchell Hashimoto <mitchell.hashimoto@gmail.com>2022-11-04 20:27:48 -0700
committerMitchell Hashimoto <mitchell.hashimoto@gmail.com>2022-11-05 19:31:02 -0700
commitb100406a6eaaaf6f92b0711141102e03f8f103bc (patch)
tree6f2dc84169451ebe84af1ac8bf1415ce48666d22 /src/termio/Thread.zig
parenta8e7c520414be26a1ee424c3fd77561cd5ec8dc7 (diff)
termio: start the thread mailbox, hook up resize
Diffstat (limited to 'src/termio/Thread.zig')
-rw-r--r--src/termio/Thread.zig50
1 files changed, 44 insertions, 6 deletions
diff --git a/src/termio/Thread.zig b/src/termio/Thread.zig
index 9e81aa482..12949edcb 100644
--- a/src/termio/Thread.zig
+++ b/src/termio/Thread.zig
@@ -6,10 +6,16 @@ const std = @import("std");
const builtin = @import("builtin");
const libuv = @import("libuv");
const termio = @import("../termio.zig");
+const BlockingQueue = @import("../blocking_queue.zig").BlockingQueue;
const Allocator = std.mem.Allocator;
const log = std.log.scoped(.io_thread);
+/// The type used for sending messages to the IO thread. For now this is
+/// hardcoded with a capacity. We can make this a comptime parameter in
+/// the future if we want it configurable.
+const Mailbox = BlockingQueue(termio.message.IO, 64);
+
/// The main event loop for the thread. The user data of this loop
/// is always the allocator used to create the loop. This is a convenience
/// so that users of the loop always have an allocator.
@@ -24,6 +30,10 @@ stop: libuv.Async,
/// The underlying IO implementation.
impl: *termio.Impl,
+/// The mailbox that can be used to send this thread messages. Note
+/// this is a blocking queue so if it is full you will get errors (or block).
+mailbox: *Mailbox,
+
/// Initialize the thread. This does not START the thread. This only sets
/// up all the internal state necessary prior to starting the thread. It
/// is up to the caller to start the thread with the threadMain entrypoint.
@@ -60,11 +70,16 @@ pub fn init(
}
}).callback);
+ // The mailbox for messaging this thread
+ var mailbox = try Mailbox.create(alloc);
+ errdefer mailbox.destroy(alloc);
+
return Thread{
.loop = loop,
.wakeup = wakeup_h,
.stop = stop_h,
.impl = impl,
+ .mailbox = mailbox,
};
}
@@ -95,6 +110,9 @@ pub fn deinit(self: *Thread) void {
_ = self.loop.run(.default) catch |err|
log.err("error finalizing event loop: {}", .{err});
+ // Nothing can possibly access the mailbox anymore, destroy it.
+ self.mailbox.destroy(alloc);
+
// Dealloc our allocator copy
alloc.destroy(alloc_ptr);
@@ -127,13 +145,33 @@ fn threadMain_(self: *Thread) !void {
_ = try self.loop.run(.default);
}
+/// Drain the mailbox, handling all the messages in our terminal implementation.
+fn drainMailbox(self: *Thread) !void {
+ // This holds the mailbox lock for the duration of the drain. The
+ // expectation is that all our message handlers will be non-blocking
+ // ENOUGH to not mess up throughput on producers.
+ var drain = self.mailbox.drain();
+ defer drain.deinit();
+
+ while (drain.next()) |message| {
+ log.debug("mailbox message={}", .{message});
+ switch (message) {
+ .resize => |v| try self.impl.resize(v.grid_size, v.screen_size),
+ }
+ }
+}
+
fn wakeupCallback(h: *libuv.Async) void {
- _ = h;
- // const t = h.getData(Thread) orelse {
- // // This shouldn't happen so we log it.
- // log.warn("render callback fired without data set", .{});
- // return;
- // };
+ const t = h.getData(Thread) orelse {
+ // This shouldn't happen so we log it.
+ log.warn("wakeup callback fired without data set", .{});
+ return;
+ };
+
+ // When we wake up, we check the mailbox. Mailbox producers should
+ // wake up our thread after publishing.
+ t.drainMailbox() catch |err|
+ log.err("error draining mailbox err={}", .{err});
}
fn stopCallback(h: *libuv.Async) void {