Last active
July 22, 2020 19:03
-
-
Save lithdew/7f3ebdb8de9bf60b7e524a5fa180f801 to your computer and use it in GitHub Desktop.
libuv wip
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 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