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); const 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.* = .{ .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 { suspend { 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)); } } }; var loop: Loop = undefined; fn amain() !void { warn("starting amain\n", .{}); try loop.sleep(1000); warn("exiting amain\n", .{}); } pub fn main() !void { try loop.init(); var frame = async amainWrap(); var res: i32 = try loop.run(); if (res > 0) warn("uv loop exited with more than one handle ({} remaining)\n", .{res}); noasync await frame; // asserts that amainWrap() finished already } fn amainWrap() void { amain() catch |e| { std.debug.warn("{}\n", .{e}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } std.process.exit(1); }; }