mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-01-05 04:47:52 +00:00
110 lines
3.2 KiB
Zig
110 lines
3.2 KiB
Zig
//! This has a helper for embedding libuv in another event loop.
|
|
//! This is an extension of libuv and not a helper built-in to libuv
|
|
//! itself, although it uses official APIs of libuv to enable the
|
|
//! functionality.
|
|
|
|
const Embed = @This();
|
|
|
|
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const testing = std.testing;
|
|
const Allocator = std.mem.Allocator;
|
|
const Loop = @import("Loop.zig");
|
|
const Sem = @import("Sem.zig");
|
|
const Thread = @import("Thread.zig");
|
|
|
|
const TerminateAtomic = std.atomic.Atomic(bool);
|
|
|
|
loop: Loop,
|
|
sem: Sem,
|
|
terminate: TerminateAtomic,
|
|
callback: fn () void,
|
|
thread: ?Thread,
|
|
|
|
/// Initialize a new embedder. The callback is called when libuv should
|
|
/// tick. The callback should be as fast as possible.
|
|
pub fn init(alloc: Allocator, loop: Loop, callback: fn () void) !Embed {
|
|
return Embed{
|
|
.loop = loop,
|
|
.sem = try Sem.init(alloc, 0),
|
|
.terminate = TerminateAtomic.init(false),
|
|
.callback = callback,
|
|
.thread = null,
|
|
};
|
|
}
|
|
|
|
/// Deinit the embed struct. This will not automatically terminate
|
|
/// the embed thread. You must call stop manually.
|
|
pub fn deinit(self: *Embed, alloc: Allocator) void {
|
|
std.debug.assert(self.thread == null);
|
|
self.sem.deinit(alloc);
|
|
self.* = undefined;
|
|
}
|
|
|
|
/// Start the thread that runs the embed logic and calls callback
|
|
/// when the libuv loop should tick. This must only be called once.
|
|
pub fn start(self: *Embed) !void {
|
|
self.thread = try Thread.initData(self, Embed.threadMain);
|
|
}
|
|
|
|
/// Stop stops the embed thread and blocks until the thread joins.
|
|
pub fn stop(self: *Embed) !void {
|
|
var thread = self.thread orelse return;
|
|
|
|
// Mark that we want to terminate
|
|
self.terminate.store(true, .SeqCst);
|
|
|
|
// Post to the semaphore to ensure that any waits are processed.
|
|
self.sem.post();
|
|
|
|
// Wait
|
|
try thread.join();
|
|
self.thread = null;
|
|
}
|
|
|
|
/// loopRun runs the next tick of the libuv event loop. This should be
|
|
/// called by the main loop thread as a result of callback making some
|
|
/// signal. This should NOT be called from callback.
|
|
pub fn loopRun(self: Embed) !void {
|
|
_ = try self.loop.run(.nowait);
|
|
self.sem.post();
|
|
}
|
|
|
|
fn threadMain(self: *Embed) void {
|
|
while (self.terminate.load(.SeqCst) == false) {
|
|
const fd = self.loop.backendFd() catch unreachable;
|
|
const timeout = self.loop.backendTimeout();
|
|
switch (builtin.os.tag) {
|
|
// epoll
|
|
.linux => {
|
|
std.log.info("FOO {} {}", .{ fd, timeout });
|
|
var ev: [1]std.os.linux.epoll_event = undefined;
|
|
while (std.os.epoll_wait(fd, &ev, timeout) == -1) {}
|
|
},
|
|
|
|
else => @compileError("unsupported libuv Embed platform"),
|
|
}
|
|
|
|
// Call our trigger
|
|
self.callback();
|
|
|
|
// Wait for libuv to run a tick
|
|
self.sem.wait();
|
|
}
|
|
}
|
|
|
|
test "Embed" {
|
|
var loop = try Loop.init(testing.allocator);
|
|
defer loop.deinit(testing.allocator);
|
|
|
|
var embed = try init(testing.allocator, loop, (struct {
|
|
fn callback() void {}
|
|
}).callback);
|
|
defer embed.deinit(testing.allocator);
|
|
|
|
// This just tests that the thread can start and then stop.
|
|
// It doesn't do much else at the moment
|
|
try embed.start();
|
|
try embed.stop();
|
|
}
|