use async handles to more immediately exit the event loop

This commit is contained in:
Mitchell Hashimoto
2022-04-22 15:42:08 -07:00
parent cd602b660c
commit 0b689723f7
3 changed files with 23 additions and 19 deletions

View File

@ -55,7 +55,8 @@ pub fn deinit(self: *App) void {
// Run the loop one more time, because destroying our other things // Run the loop one more time, because destroying our other things
// like windows usually cancel all our event loop stuff and we need // like windows usually cancel all our event loop stuff and we need
// one more run through to finalize all the closes. // one more run through to finalize all the closes.
_ = self.loop.run(.default) catch unreachable; _ = self.loop.run(.default) catch |err|
log.err("error finalizing event loop: {}", .{err});
// Dealloc our allocator copy // Dealloc our allocator copy
self.alloc.destroy(self.loop.getData(Allocator).?); self.alloc.destroy(self.loop.getData(Allocator).?);
@ -76,13 +77,11 @@ pub fn run(self: App) !void {
defer embed.deinit(self.alloc); defer embed.deinit(self.alloc);
try embed.start(); try embed.start();
// We need at least one handle in the event loop at all times so // This async handle is used to "wake up" the embed thread so we can
// that the loop doesn't spin 100% CPU. // exit immediately once the windows want to close.
var timer = try libuv.Timer.init(self.alloc, self.loop); var async_h = try libuv.Async.init(self.alloc, self.loop, (struct {
errdefer timer.deinit(self.alloc); fn callback(_: *libuv.Async) void {}
try timer.start((struct { }).callback);
fn callback(_: *libuv.Timer) void {}
}).callback, 5000, 5000);
while (!self.window.shouldClose()) { while (!self.window.shouldClose()) {
try self.window.run(); try self.window.run();
@ -95,14 +94,19 @@ pub fn run(self: App) !void {
try embed.loopRun(); try embed.loopRun();
} }
// Close our timer so that we can cleanly close the loop. // Notify the embed thread to stop. We do this before we send on the
timer.close((struct { // async handle so that when the thread goes around it exits.
fn callback(t: *libuv.Timer) void { embed.stop();
const alloc = t.loop().getData(Allocator).?.*;
t.deinit(alloc); // Wake up the event loop and schedule our close.
try async_h.send();
async_h.close((struct {
fn callback(h: *libuv.Async) void {
const alloc = h.loop().getData(Allocator).?.*;
h.deinit(alloc);
} }
}).callback); }).callback);
embed.stop(); // Wait for the thread to end which should be almost instant.
try embed.join(); try embed.join();
} }

View File

@ -14,14 +14,14 @@ handle: *c.uv_async_t,
pub usingnamespace Handle(Async); pub usingnamespace Handle(Async);
pub fn init(alloc: Allocator, loop: Loop, comptime cb: fn (Async) void) !Async { pub fn init(alloc: Allocator, loop: Loop, comptime cb: fn (*Async) void) !Async {
var handle = try alloc.create(c.uv_async_t); var handle = try alloc.create(c.uv_async_t);
errdefer alloc.destroy(handle); errdefer alloc.destroy(handle);
const Wrapper = struct { const Wrapper = struct {
pub fn callback(arg: [*c]c.uv_async_t) callconv(.C) void { pub fn callback(arg: [*c]c.uv_async_t) callconv(.C) void {
const newSelf: Async = .{ .handle = arg }; var newSelf: Async = .{ .handle = arg };
@call(.{ .modifier = .always_inline }, cb, .{newSelf}); @call(.{ .modifier = .always_inline }, cb, .{&newSelf});
} }
}; };
@ -43,7 +43,7 @@ test "Async" {
var loop = try Loop.init(testing.allocator); var loop = try Loop.init(testing.allocator);
defer loop.deinit(testing.allocator); defer loop.deinit(testing.allocator);
var h = try init(testing.allocator, loop, (struct { var h = try init(testing.allocator, loop, (struct {
fn callback(v: Async) void { fn callback(v: *Async) void {
v.close(null); v.close(null);
} }
}).callback); }).callback);

View File

@ -20,7 +20,7 @@ test "Async: cancel timer" {
}).callback, 5000, 5000); }).callback, 5000, 5000);
var async_handle = try libuv.Async.init(testing.allocator, loop, (struct { var async_handle = try libuv.Async.init(testing.allocator, loop, (struct {
fn callback(v: libuv.Async) void { fn callback(v: *libuv.Async) void {
v.loop().stop(); v.loop().stop();
v.close(null); v.close(null);
} }