diff --git a/src/libuv/Loop.zig b/src/libuv/Loop.zig index b748e9899..a1d10a43b 100644 --- a/src/libuv/Loop.zig +++ b/src/libuv/Loop.zig @@ -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. diff --git a/src/libuv/Timer.zig b/src/libuv/Timer.zig new file mode 100644 index 000000000..6b6db57fd --- /dev/null +++ b/src/libuv/Timer.zig @@ -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); +} diff --git a/src/libuv/handle.zig b/src/libuv/handle.zig new file mode 100644 index 000000000..bfec0e8dc --- /dev/null +++ b/src/libuv/handle.zig @@ -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, .{¶m}); + } + }).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; + } + }; +} diff --git a/src/libuv/main.zig b/src/libuv/main.zig index 5695f343b..66d24d352 100644 --- a/src/libuv/main.zig +++ b/src/libuv/main.zig @@ -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;