Skip to content

Instantly share code, notes, and snippets.

@lithdew
Last active July 22, 2020 19:03
Show Gist options
  • Save lithdew/7f3ebdb8de9bf60b7e524a5fa180f801 to your computer and use it in GitHub Desktop.
Save lithdew/7f3ebdb8de9bf60b7e524a5fa180f801 to your computer and use it in GitHub Desktop.
libuv wip
const std = @import("std");
const c = @cImport(@cInclude("uv.h"));
const assert = std.debug.assert;
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
pub fn onClose(handle: ?*c.uv_handle_t) callconv(.C) void {
const allocator = &arena.allocator; // get allocator here
allocator.destroy(handle.?);
}
pub fn afterShutdown(req: ?*c.uv_shutdown_t, status: c_int) callconv(.C) void {
const allocator = &arena.allocator; // get allocator here
if (status < 0) {
std.debug.warn("Shutdown error: {}\n", .{std.mem.span(c.uv_strerror(status))});
}
c.uv_close(@ptrCast(*c.uv_handle_t, req.?.handle), onClose);
allocator.destroy(req);
std.debug.print("A connection has been closed.\n", .{});
}
pub fn afterWrite(req: ?*c.uv_write_t, status: c_int) callconv(.C) void {
const allocator = &arena.allocator; // get allocator here
const wr = @ptrCast(*WriteRequest, req);
if (wr.buf.base != null) allocator.destroy(wr.buf.base);
allocator.destroy(wr);
if (status == 0) return;
std.debug.warn("uv_write error: {}\n", .{std.mem.span(c.uv_strerror(status))});
if (status == c.UV_ECANCELED) return;
assert(status == c.UV_EPIPE);
c.uv_close(@ptrCast(*c.uv_handle_t, req.?.handle), onClose);
}
const WriteRequest = struct {
req: c.uv_write_t,
buf: c.uv_buf_t,
};
pub fn afterRead(handle: ?*c.uv_stream_t, nread: isize, buf: ?*const c.uv_buf_t) callconv(.C) void {
const allocator = &arena.allocator; // get allocator here
if (nread <= 0 and buf.?.base != null) allocator.destroy(buf);
if (nread == 0) return;
if (nread < 0) {
if (nread != c.UV_EOF) std.debug.warn("err: {}, {}\n", .{ nread, std.mem.span(c.uv_strerror(@intCast(c_int, nread))) });
const req = allocator.create(c.uv_shutdown_t) catch unreachable;
assert(c.uv_shutdown(req, handle, afterShutdown) == 0);
return;
}
const data = std.mem.trimRight(u8, buf.?.base[0..@intCast(usize, nread)], "\n");
std.debug.print("Got data: {}\n", .{data});
// send text back
const wr = allocator.create(WriteRequest) catch unreachable;
wr.buf = c.uv_buf_init(buf.?.base, @intCast(c_uint, nread));
assert(c.uv_write(&wr.req, handle, &wr.buf, 1, afterWrite) == 0);
}
pub fn onAllocate(handle: ?*c.uv_handle_t, suggested_size: usize, buf: ?*c.uv_buf_t) callconv(.C) void {
const allocator = &arena.allocator; // get allocator here
const slice = allocator.alloc(u8, suggested_size) catch unreachable;
buf.?.base = slice.ptr;
buf.?.len = slice.len;
}
pub fn onConnection(server: ?*c.uv_stream_t, status: c_int) callconv(.C) void {
assert(status == 0);
const allocator = &arena.allocator; // get allocator here
const stream = allocator.create(c.uv_tcp_t) catch unreachable;
assert(c.uv_tcp_init(c.uv_default_loop(), stream) == 0);
stream.data = server;
if (c.uv_accept(server, @ptrCast(*c.uv_stream_t, stream)) != 0) {
c.uv_close(@ptrCast(*c.uv_handle_t, stream), onClose);
return;
}
var raw_addr: std.os.sockaddr align(4) = undefined;
var raw_addr_len: c_int = @sizeOf(@TypeOf(raw_addr));
assert(c.uv_tcp_getpeername(stream, @ptrCast(*c.sockaddr, &raw_addr), &raw_addr_len) == 0);
var addr = std.net.Address.initPosix(&raw_addr);
std.debug.print("New connection incoming: {}\n", .{addr});
assert(c.uv_read_start(@ptrCast(*c.uv_stream_t, stream), onAllocate, afterRead) == 0);
}
pub fn onInterrupt(handle: ?*c.uv_signal_t, signum: c_int) callconv(.C) void {
assert(signum == std.os.SIGINT);
const allocator = &arena.allocator; // get allocator here
allocator.destroy(handle);
std.debug.print("\nShutting down...\n", .{});
if (c.uv_loop_close(handle.?.loop) == c.UV_EBUSY) {
const impl = struct {
fn inner(pending: ?*c.uv_handle_t, arg: ?*c_void) callconv(.C) void {
c.uv_close(pending, onClose);
}
};
c.uv_walk(handle.?.loop, impl.inner, null);
}
}
pub fn startServer(allocator: *std.mem.Allocator) !void {
var addr = try std.net.Address.parseIp("0.0.0.0", 9000);
std.debug.print("Listening for new connections on '{}'.\n", .{addr});
var server = try allocator.create(c.uv_tcp_t);
assert(c.uv_tcp_init(c.uv_default_loop(), server) == 0);
assert(c.uv_tcp_bind(server, @ptrCast(*c.sockaddr, &addr.any), 0) == 0);
assert(c.uv_listen(@ptrCast(*c.uv_stream_t, server), 128, onConnection) == 0);
}
pub fn startSignalHandler(allocator: *std.mem.Allocator) !void {
var signal = try allocator.create(c.uv_signal_t);
assert(c.uv_signal_init(c.uv_default_loop(), signal) == 0);
assert(c.uv_signal_start_oneshot(signal, onInterrupt, std.os.SIGINT) == 0);
}
pub fn main() !void {
defer arena.deinit();
defer c.uv_library_shutdown();
try startServer(&arena.allocator);
try startSignalHandler(&arena.allocator);
assert(c.uv_run(c.uv_default_loop(), .UV_RUN_DEFAULT) == 0);
std.debug.print("Server has shutdown.", .{});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment