libuv: Timers

This commit is contained in:
Mitchell Hashimoto
2022-04-21 19:32:48 -07:00
parent 4b0cddc948
commit 10440d6783
4 changed files with 123 additions and 0 deletions

View File

@ -30,6 +30,13 @@ pub fn deinit(self: *Loop, alloc: Allocator) void {
self.* = undefined;
}
/// Returns true if the loop is still alive.
pub fn alive(self: Loop) !bool {
const res = c.uv_loop_alive(self.loop);
try errors.convertError(res);
return res > 0;
}
/// This function runs the event loop. See RunMode for mode documentation.
///
/// This is not reentrant. It must not be called from a callback.

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

@ -0,0 +1,54 @@
//! Timer handles are used to schedule callbacks to be called in the future.
const Timer = @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_timer_t,
pub usingnamespace Handle(Timer);
pub fn init(alloc: Allocator, loop: Loop) !Timer {
var timer = try alloc.create(c.uv_timer_t);
errdefer alloc.destroy(timer);
try errors.convertError(c.uv_timer_init(loop.loop, timer));
return Timer{ .handle = timer };
}
pub fn deinit(self: *Timer, alloc: Allocator) void {
alloc.destroy(self.handle);
self.* = undefined;
}
test "Timer" {
var loop = try Loop.init(testing.allocator);
defer loop.deinit(testing.allocator);
var timer = try init(testing.allocator, loop);
defer timer.deinit(testing.allocator);
timer.close(null);
_ = try loop.run(.default);
}
test "Timer: close callback" {
var loop = try Loop.init(testing.allocator);
defer loop.deinit(testing.allocator);
var timer = try init(testing.allocator, loop);
defer timer.deinit(testing.allocator);
var data: u8 = 42;
timer.setData(&data);
timer.close((struct {
fn callback(v: *Timer) void {
var dataPtr = v.getData(u8).?;
dataPtr.* = 24;
}
}).callback);
_ = try loop.run(.default);
try testing.expectEqual(@as(u8, 24), data);
}

60
src/libuv/handle.zig Normal file
View File

@ -0,0 +1,60 @@
const c = @import("c.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
/// handler functions to other handle types.
pub fn Handle(comptime T: type) type {
// 1. T should be a struct
// 2. First field should be the handle pointer
const tInfo = @typeInfo(T).Struct;
const HandleType = tInfo.fields[0].field_type;
return struct {
// Request handle to be closed. close_cb will be called asynchronously
// after this call. This MUST be called on each handle before memory
// is released. Moreover, the memory can only be released in close_cb
// or after it has returned.
//
// Handles that wrap file descriptors are closed immediately but
// close_cb will still be deferred to the next iteration of the event
// loop. It gives you a chance to free up any resources associated with
// the handle.
//
// In-progress requests, like uv_connect_t or uv_write_t, are cancelled
// and have their callbacks called asynchronously with status=UV_ECANCELED.
pub fn close(self: *T, comptime cb: ?fn (*T) void) void {
const cbParam = if (cb) |f|
(struct {
pub fn callback(handle: [*c]c.uv_handle_t) callconv(.C) void {
// We get the raw handle, so we need to reconstruct
// the T. This is mutable because a lot of the libuv APIs
// are non-const but modifying it makes no sense.
var param: T = .{ .handle = @ptrCast(HandleType, handle) };
@call(.{ .modifier = .always_inline }, f, .{&param});
}
}).callback
else
null;
c.uv_close(@ptrCast(*c.uv_handle_t, self.handle), cbParam);
}
/// Sets handle->data to data.
pub fn setData(self: *T, pointer: ?*anyopaque) void {
c.uv_handle_set_data(
@ptrCast(*c.uv_handle_t, self.handle),
pointer,
);
}
/// Returns handle->data.
pub fn getData(self: *T, comptime DT: type) ?*DT {
return if (c.uv_handle_get_data(@ptrCast(*c.uv_handle_t, self.handle))) |ptr|
@ptrCast(?*DT, @alignCast(@alignOf(DT), ptr))
else
null;
}
};
}

View File

@ -1,10 +1,12 @@
const Loop = @import("Loop.zig");
const Timer = @import("Timer.zig");
const Sem = @import("Sem.zig");
const Thread = @import("Thread.zig");
const Error = @import("error.zig").Error;
test {
_ = Loop;
_ = Timer;
_ = Sem;
_ = Thread;
_ = Error;