FlexiLib/src/main.zig

161 lines
5.5 KiB
Zig
Raw Normal View History

2023-05-07 15:59:38 -07:00
const std = @import("std");
2023-05-09 09:18:38 -07:00
const c = @cImport({
@cInclude("dlfcn.h");
});
const Watch = @import("Watch.zig");
const serve_op = *const fn () void;
var shutdown = false;
const timeout = 250;
const Executor = struct {
path: [:0]const u8,
library: ?*anyopaque = null,
serve: ?serve_op = null,
watch: ?usize = null,
};
var executors = [_]Executor{.{
.path = "/home/lobo/home/faas-proxy/zig-out/lib/libfaas-proxy-sample-lib.so",
}};
var watcher = Watch.init(executorChanged);
fn serve() !void {
// if (some path routing thing) {
(try getExecutor(0))();
// if (std.c.dlerror()) |_| { // TODO: use capture
// return error.CouldNotLoadSymbolServe;
// }
// TODO: only close on reload
// if (std.c.dlclose(library.?) != 0) {
// return error.CouldNotUnloadLibrary;
// }
// library = null;
// }
}
fn getExecutor(key: usize) !serve_op {
var executor = &executors[key];
if (executor.serve) |s| return s;
executor.library = blk: {
if (executor.library) |l| {
break :blk l;
}
const l = try dlopen(executor.path);
errdefer if (std.c.dlclose(l) != 0)
@panic("System unstable: Error after library open and cannot close");
executor.watch = try watcher.addFileWatch(executor.path);
break :blk l;
};
// std.c.dlerror();
const serve_function = std.c.dlsym(executor.library.?, "serve");
if (serve_function == null) return error.CouldNotLoadSymbolServe;
executor.serve = @ptrCast(serve_op, serve_function.?);
return executor.serve.?;
}
// This works
fn executorChanged(watch: usize) void {
for (&executors) |*executor| {
if (executor.watch) |w| {
if (w == watch) {
if (executor.library) |l| {
// TODO: These two lines could introduce a race. Right now that would mean a panic
executor.serve = null;
if (std.c.dlclose(l) != 0)
@panic("System unstable: Error after library open and cannot close");
}
executor.library = null;
executor.serve = null;
}
}
}
}
// NOTE: this will be on a different thread. This code does not work, and I
// am fairly certain it is because we can't share a function pointer between
// threads
// fn executorChanged(watch: usize) void {
// std.debug.print("executor with watch {d} changed\n", .{watch});
// for (&executors) |*executor| {
// if (executor.watch) |w| {
// if (w == watch) {
// if (executor.library) |l| {
// std.debug.print("reloading executor at path: {s}\n", .{executor.path});
// const newlib = dlopen(executor.path) catch {
// std.debug.print("could not reload! error opening library\n", .{});
// return;
// };
// errdefer if (std.c.dlclose(newlib) != 0)
// @panic("System unstable: Error after library open and cannot close");
// const serve_function = std.c.dlsym(newlib, "serve");
// if (serve_function == null) {
// std.debug.print("could not reload! error finding symbol\n", .{});
// return;
// }
// // new lib all loaded up - do the swap and close the old
// std.debug.print("updating function and library\n", .{});
// executor.serve = @ptrCast(serve_op, serve_function.?);
// executor.library = newlib;
// if (std.c.dlclose(l) != 0)
// @panic("System unstable: Error after library open and cannot close");
// std.debug.print("closed old library\n", .{});
// }
// }
// }
// }
// }
fn dlopen(path: [:0]const u8) !*anyopaque {
// We need now (and local) because we're about to call it
const lib = std.c.dlopen(path, c.RTLD_NOW);
if (lib) |l| return l;
return error.CouldNotOpenDynamicLibrary;
}
2023-05-07 15:59:38 -07:00
pub fn main() !void {
2023-05-09 09:18:38 -07:00
defer watcher.deinit();
2023-05-07 15:59:38 -07:00
// stdout is for the actual output of your application, for example if you
// are implementing gzip, then only the compressed bytes should be sent to
// stdout, not any debugging messages.
const stdout_file = std.io.getStdOut().writer();
var bw = std.io.bufferedWriter(stdout_file);
const stdout = bw.writer();
2023-05-09 09:18:38 -07:00
const stderr_file = std.io.getStdErr().writer();
var bw_stderr = std.io.bufferedWriter(stderr_file);
const stderr = bw_stderr.writer();
2023-05-07 15:59:38 -07:00
try stdout.print("Run `zig build test` to run the tests.\n", .{});
try bw.flush(); // don't forget to flush!
2023-05-09 09:18:38 -07:00
const watcher_thread = try std.Thread.spawn(.{}, Watch.watchFds, .{&watcher});
while (true) {
std.time.sleep(std.time.ns_per_s * 2);
try stdout.print("Serving...", .{});
try bw.flush();
serve() catch |err| {
try stderr.print("Error serving request ({any})\n", .{err});
try bw_stderr.flush();
};
try stdout.print("served\n", .{});
try bw.flush();
}
shutdown = true;
watcher_thread.join();
2023-05-07 15:59:38 -07:00
}
test "simple test" {
var list = std.ArrayList(i32).init(std.testing.allocator);
defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
try list.append(42);
try std.testing.expectEqual(@as(i32, 42), list.pop());
}