Skip to content

Instantly share code, notes, and snippets.

@Qix-
Created March 26, 2020 20:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Qix-/a53c46b0ff25a38a24a49c1dcea28d54 to your computer and use it in GitHub Desktop.
Save Qix-/a53c46b0ff25a38a24a49c1dcea28d54 to your computer and use it in GitHub Desktop.
Simplistic libuv wrapper example in Zig
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