libuv: Async

This commit is contained in:
Mitchell Hashimoto
2022-04-22 11:57:11 -07:00
parent cca32c4d1c
commit ccf95d823e
5 changed files with 110 additions and 0 deletions

54
src/libuv/Async.zig Normal file
View File

@ -0,0 +1,54 @@
//! Async handles allow the user to wakeup the event loop and get a callback
//! called from another thread.
const Async = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const testing = std.testing;
const c = @import("c.zig");
const errors = @import("error.zig");
const Loop = @import("Loop.zig");
const Handle = @import("handle.zig").Handle;
handle: *c.uv_async_t,
pub usingnamespace Handle(Async);
pub fn init(alloc: Allocator, loop: Loop, comptime cb: fn (Async) void) !Async {
var handle = try alloc.create(c.uv_async_t);
errdefer alloc.destroy(handle);
const Wrapper = struct {
pub fn callback(arg: [*c]c.uv_async_t) callconv(.C) void {
const newSelf: Async = .{ .handle = arg };
@call(.{ .modifier = .always_inline }, cb, .{newSelf});
}
};
try errors.convertError(c.uv_async_init(loop.loop, handle, Wrapper.callback));
return Async{ .handle = handle };
}
pub fn deinit(self: *Async, alloc: Allocator) void {
alloc.destroy(self.handle);
self.* = undefined;
}
/// Wake up the event loop and call the async handles callback.
pub fn send(self: Async) !void {
try errors.convertError(c.uv_async_send(self.handle));
}
test "Async" {
var loop = try Loop.init(testing.allocator);
defer loop.deinit(testing.allocator);
var h = try init(testing.allocator, loop, (struct {
fn callback(v: Async) void {
v.close(null);
}
}).callback);
defer h.deinit(testing.allocator);
try h.send();
_ = try loop.run(.default);
}

View File

@ -46,6 +46,13 @@ pub fn run(self: Loop, mode: RunMode) !u32 {
return @intCast(u32, res);
}
/// Stop the event loop, causing uv_run() to end as soon as possible. This
/// will happen not sooner than the next loop iteration. If this function was
/// called before blocking for i/o, the loop wont block for i/o on this iteration.
pub fn stop(self: Loop) void {
c.uv_stop(self.loop);
}
/// Get backend file descriptor. Only kqueue, epoll and event ports are supported.
///
/// This can be used in conjunction with uv_run(loop, UV_RUN_NOWAIT) to poll

View File

@ -1,5 +1,7 @@
const c = @import("c.zig");
const Loop = @import("Loop.zig");
/// Returns a struct that has all the shared handle functions for the
/// given handle type T. The type T must have a field named "handle".
/// This is expected to be used with usingnamespace to add the shared
@ -40,6 +42,12 @@ pub fn Handle(comptime T: type) type {
c.uv_close(@ptrCast(*c.uv_handle_t, self.handle), cbParam);
}
/// Loop returns the loop that this handle is a part of.
pub fn loop(self: T) Loop {
const handle = @ptrCast(*c.uv_handle_t, self.handle);
return .{ .loop = c.uv_handle_get_loop(handle) };
}
/// Sets handle->data to data.
pub fn setData(self: T, pointer: ?*anyopaque) void {
c.uv_handle_set_data(

View File

@ -1,4 +1,5 @@
pub const Loop = @import("Loop.zig");
pub const Async = @import("Async.zig");
pub const Timer = @import("Timer.zig");
pub const Sem = @import("Sem.zig");
pub const Thread = @import("Thread.zig");
@ -7,7 +8,10 @@ pub const Error = @import("error.zig").Error;
pub const Embed = @import("Embed.zig");
test {
_ = @import("tests.zig");
_ = Loop;
_ = Async;
_ = Timer;
_ = Sem;
_ = Thread;

37
src/libuv/tests.zig Normal file
View File

@ -0,0 +1,37 @@
//! This file contains other behavior tests for the libuv integration.
//! We trust that libuv works, but still test some behaviors to ensure
//! that our wrappers around libuv are working as expected.
const std = @import("std");
const testing = std.testing;
const libuv = @import("main.zig");
test "Async: cancel timer" {
const alloc = testing.allocator;
var loop = try libuv.Loop.init(alloc);
defer loop.deinit(alloc);
var timer = try libuv.Timer.init(alloc, loop);
defer timer.deinit(alloc);
// Start a timer with a long timeout. This will block our loop.
try timer.start((struct {
fn callback(_: libuv.Timer) void {}
}).callback, 5000, 5000);
var async_handle = try libuv.Async.init(testing.allocator, loop, (struct {
fn callback(v: libuv.Async) void {
v.loop().stop();
v.close(null);
}
}).callback);
defer async_handle.deinit(testing.allocator);
try async_handle.send();
// This run through the loop should exit because we called loop stop.
_ = try loop.run(.default);
// We need to run the loop one more time to handle all our close callbacks.
timer.close(null);
_ = try loop.run(.default);
}