Created
March 26, 2020 20:56
-
-
Save Qix-/a53c46b0ff25a38a24a49c1dcea28d54 to your computer and use it in GitHub Desktop.
Simplistic libuv wrapper example in Zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const warn = @import("std").debug.warn; // XXX DEBUG | |
const assert = std.debug.assert; | |
const c = @cImport({ | |
@cInclude("uv.h"); | |
}); | |
pub const UVError = error{ E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EAFNOSUPPORT, EAGAIN, EAI_ADDRFAMILY, EAI_AGAIN, EAI_BADFLAGS, EAI_BADHINTS, EAI_CANCELED, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_NODATA, EAI_NONAME, EAI_OVERFLOW, EAI_PROTOCOL, EAI_SERVICE, EAI_SOCKTYPE, EALREADY, EBADF, EBUSY, ECANCELED, ECHARSET, ECONNABORTED, ECONNREFUSED, ECONNRESET, EDESTADDRREQ, EEXIST, EFAULT, EFBIG, EHOSTUNREACH, EINTR, EINVAL, EIO, EISCONN, EISDIR, ELOOP, EMFILE, EMSGSIZE, ENAMETOOLONG, ENETDOWN, ENETUNREACH, ENFILE, ENOBUFS, ENODEV, ENOENT, ENOMEM, ENONET, ENOPROTOOPT, ENOSPC, ENOSYS, ENOTCONN, ENOTDIR, ENOTEMPTY, ENOTSOCK, ENOTSUP, EPERM, EPIPE, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EROFS, ESHUTDOWN, ESPIPE, ESRCH, ETIMEDOUT, ETXTBSY, EXDEV, UNKNOWN, EOF, ENXIO, EMLINK }; | |
fn check_uv(r: i32) !void { | |
if (r >= 0) return; | |
return switch (r) { | |
// All positive (including 0) numbers | |
c.UV_E2BIG => UVError.E2BIG, | |
c.UV_EACCES => UVError.EACCES, | |
c.UV_EADDRINUSE => UVError.EADDRINUSE, | |
c.UV_EADDRNOTAVAIL => UVError.EADDRNOTAVAIL, | |
c.UV_EAFNOSUPPORT => UVError.EAFNOSUPPORT, | |
c.UV_EAGAIN => UVError.EAGAIN, | |
c.UV_EAI_ADDRFAMILY => UVError.EAI_ADDRFAMILY, | |
c.UV_EAI_AGAIN => UVError.EAI_AGAIN, | |
c.UV_EAI_BADFLAGS => UVError.EAI_BADFLAGS, | |
c.UV_EAI_BADHINTS => UVError.EAI_BADHINTS, | |
c.UV_EAI_CANCELED => UVError.EAI_CANCELED, | |
c.UV_EAI_FAIL => UVError.EAI_FAIL, | |
c.UV_EAI_FAMILY => UVError.EAI_FAMILY, | |
c.UV_EAI_MEMORY => UVError.EAI_MEMORY, | |
c.UV_EAI_NODATA => UVError.EAI_NODATA, | |
c.UV_EAI_NONAME => UVError.EAI_NONAME, | |
c.UV_EAI_OVERFLOW => UVError.EAI_OVERFLOW, | |
c.UV_EAI_PROTOCOL => UVError.EAI_PROTOCOL, | |
c.UV_EAI_SERVICE => UVError.EAI_SERVICE, | |
c.UV_EAI_SOCKTYPE => UVError.EAI_SOCKTYPE, | |
c.UV_EALREADY => UVError.EALREADY, | |
c.UV_EBADF => UVError.EBADF, | |
c.UV_EBUSY => UVError.EBUSY, | |
c.UV_ECANCELED => UVError.ECANCELED, | |
c.UV_ECHARSET => UVError.ECHARSET, | |
c.UV_ECONNABORTED => UVError.ECONNABORTED, | |
c.UV_ECONNREFUSED => UVError.ECONNREFUSED, | |
c.UV_ECONNRESET => UVError.ECONNRESET, | |
c.UV_EDESTADDRREQ => UVError.EDESTADDRREQ, | |
c.UV_EEXIST => UVError.EEXIST, | |
c.UV_EFAULT => UVError.EFAULT, | |
c.UV_EFBIG => UVError.EFBIG, | |
c.UV_EHOSTUNREACH => UVError.EHOSTUNREACH, | |
c.UV_EINTR => UVError.EINTR, | |
c.UV_EINVAL => UVError.EINVAL, | |
c.UV_EIO => UVError.EIO, | |
c.UV_EISCONN => UVError.EISCONN, | |
c.UV_EISDIR => UVError.EISDIR, | |
c.UV_ELOOP => UVError.ELOOP, | |
c.UV_EMFILE => UVError.EMFILE, | |
c.UV_EMSGSIZE => UVError.EMSGSIZE, | |
c.UV_ENAMETOOLONG => UVError.ENAMETOOLONG, | |
c.UV_ENETDOWN => UVError.ENETDOWN, | |
c.UV_ENETUNREACH => UVError.ENETUNREACH, | |
c.UV_ENFILE => UVError.ENFILE, | |
c.UV_ENOBUFS => UVError.ENOBUFS, | |
c.UV_ENODEV => UVError.ENODEV, | |
c.UV_ENOENT => UVError.ENOENT, | |
c.UV_ENOMEM => UVError.ENOMEM, | |
c.UV_ENONET => UVError.ENONET, | |
c.UV_ENOPROTOOPT => UVError.ENOPROTOOPT, | |
c.UV_ENOSPC => UVError.ENOSPC, | |
c.UV_ENOSYS => UVError.ENOSYS, | |
c.UV_ENOTCONN => UVError.ENOTCONN, | |
c.UV_ENOTDIR => UVError.ENOTDIR, | |
c.UV_ENOTEMPTY => UVError.ENOTEMPTY, | |
c.UV_ENOTSOCK => UVError.ENOTSOCK, | |
c.UV_ENOTSUP => UVError.ENOTSUP, | |
c.UV_EPERM => UVError.EPERM, | |
c.UV_EPIPE => UVError.EPIPE, | |
c.UV_EPROTO => UVError.EPROTO, | |
c.UV_EPROTONOSUPPORT => UVError.EPROTONOSUPPORT, | |
c.UV_EPROTOTYPE => UVError.EPROTOTYPE, | |
c.UV_ERANGE => UVError.ERANGE, | |
c.UV_EROFS => UVError.EROFS, | |
c.UV_ESHUTDOWN => UVError.ESHUTDOWN, | |
c.UV_ESPIPE => UVError.ESPIPE, | |
c.UV_ESRCH => UVError.ESRCH, | |
c.UV_ETIMEDOUT => UVError.ETIMEDOUT, | |
c.UV_ETXTBSY => UVError.ETXTBSY, | |
c.UV_EXDEV => UVError.EXDEV, | |
c.UV_UNKNOWN => UVError.UNKNOWN, | |
c.UV_EOF => UVError.EOF, | |
c.UV_ENXIO => UVError.ENXIO, | |
c.UV_EMLINK => UVError.EMLINK, | |
else => unreachable, | |
}; | |
} | |
fn cb_loop_sleep(handle: ?*c.uv_timer_t) callconv(.C) void { | |
const aligned_ptr = @alignCast(@alignOf(@Frame(Loop.sleep)), handle.?.data); | |
var frame = @ptrCast(*@Frame(Loop.sleep), aligned_ptr); | |
resume frame; | |
} | |
pub const Loop = struct { | |
uv_loop: c.uv_loop_t, | |
fn init(self: *Loop) !void { | |
self.* = Loop{ .uv_loop = undefined }; | |
try check_uv(c.uv_loop_init(&self.uv_loop)); | |
} | |
fn close(self: *Loop) !void { | |
var r: i32 = c.uv_loop_close(&self.uv_loop); | |
try check_uv(r); | |
} | |
fn run(self: *Loop) !i32 { | |
var r: i32 = c.uv_run(&self.uv_loop, c.uv_run_mode.UV_RUN_DEFAULT); | |
try check_uv(r); | |
return r; | |
} | |
fn stop(self: *Loop) void { | |
c.uv_stop(&self.uv_loop); | |
} | |
fn alive(self: *Loop) bool { | |
return c.uv_loop_alive(&self.uv_loop) != 0; | |
} | |
fn backendTimeout(self: *Loop) i32 { | |
return c.uv_backend_timeout(&self.uv_loop); | |
} | |
fn now(self: *Loop) u64 { | |
return c.uv_now(&self.uv_loop); | |
} | |
fn updateTime(self: *Loop) void { | |
c.uv_update_time(&self.uv_loop); | |
} | |
fn sleep(self: *Loop, milliseconds: u64) !void { | |
var handle: c.uv_timer_t = undefined; | |
try check_uv(c.uv_timer_init(&self.uv_loop, &handle)); | |
handle.data = @frame(); | |
try check_uv(c.uv_timer_start(&handle, cb_loop_sleep, milliseconds, 0)); | |
suspend; | |
} | |
}; | |
var loop: Loop = undefined; | |
fn start_coroutine(id: i32) !void { | |
warn("coroutine {}: sleeping for 1s...\n", .{id}); | |
try loop.sleep(1000); | |
warn("coroutine {}: done!\n", .{id}); | |
} | |
fn amain() !void { | |
warn("amain: sleeping for 1s...\n", .{}); | |
try loop.sleep(1000); | |
warn("amain: starting 3 coroutines...\n", .{}); | |
var c1 = async start_coroutine(1); | |
var c2 = async start_coroutine(2); | |
var c3 = async start_coroutine(3); | |
warn("amain: waiting for coroutines to return...\n", .{}); | |
_ = await c1; | |
_ = await c2; | |
_ = await c3; | |
warn("amain: done!\n", .{}); | |
} | |
pub fn main() !void { | |
try loop.init(); | |
_ = async amainWrap(); | |
var res: i32 = try loop.run(); | |
if (res > 0) warn("uv loop exited with more than one handle ({} remaining)\n", .{res}); | |
} | |
fn amainWrap() void { | |
amain() catch |e| { | |
std.debug.warn("{}\n", .{e}); | |
if (@errorReturnTrace()) |trace| { | |
std.debug.dumpStackTrace(trace.*); | |
} | |
std.process.exit(1); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment