diff --git a/src/bar.zig b/src/bar.zig index ca32e3b..3fca266 100644 --- a/src/bar.zig +++ b/src/bar.zig @@ -5,9 +5,9 @@ const Self = @This(); export const MODULE = core.module(Self); -pub fn startup(_: std.mem.Allocator) !Self { +pub fn startup(alloc: std.mem.Allocator) !*Self { std.log.debug("!! startup {s} !!", .{@typeName(@This())}); - return .{}; + return try alloc.create(Self); } pub fn unload(_: *Self, _: std.mem.Allocator) ![]u8 { @@ -19,6 +19,7 @@ pub fn reload(_: *Self, _: std.mem.Allocator, _: []u8) !void { std.log.debug("!! reload {s} !!", .{@typeName(@This())}); } -pub fn shutdown(_: *Self, _: std.mem.Allocator) void { +pub fn shutdown(self: *Self, alloc: std.mem.Allocator) void { std.log.debug("!! shutdown {s} !!", .{@typeName(@This())}); + alloc.destroy(self); } diff --git a/src/core.zig b/src/core.zig index 06dc8a0..e095305 100644 --- a/src/core.zig +++ b/src/core.zig @@ -38,129 +38,92 @@ const std = @import("std"); // Things should be done in a thread-safe manner. Signal to a module that it should unload gracefully, and the // module signal when it can safely be unloaded. -pub fn ModuleType(Self: type) type { +pub fn CFunc(Function: type) type { + const Arguments = std.meta.ArgsTuple(Function); + const Return = @typeInfo(Function).@"fn".return_type.?; + return fn (*const Arguments, *Return) callconv(.c) void; +} + +pub fn hook(impl: anytype) CFunc(@TypeOf(impl)) { + const Function = @TypeOf(impl); + const Arguments = std.meta.ArgsTuple(Function); + const Return = @typeInfo(Function).@"fn".return_type.?; + + return struct { + pub fn cimpl(args: *const Arguments, ret: *Return) callconv(.c) void { + ret.* = @call(.auto, impl, .{args.*}); + } + }.cimpl; +} + +// pub fn Func(PFunction: type) type { +// const CFunction = @typeInfo(PFunction).pointer.child; +// const F = @typeInfo(CFunction).@"fn"; +// const PArgs = F.params[0].type.?; +// const PRet = F.params[1].type.?; +// const Args = @typeInfo(PArgs).pointer.child; +// const Ret = @typeInfo(PRet).pointer.child; +// return fn (Args) Ret; +// } + +// pub fn slot(cimpl: anytype) Func(@TypeOf(cimpl)) { +// return struct{ +// pub fn impl(args: ) +// }.impl; +// } + +pub fn Slot(Function: type) type { + const Arguments = std.meta.ArgsTuple(Function); + const Return = @typeInfo(Function).@"fn".return_type.?; + return extern struct { - pub const Startup = fn (alloc: std.mem.Allocator) anyerror!Self; - pub const CStartup = fn (alloc: *const std.mem.Allocator) callconv(.c) ?*anyopaque; + ptr: *const fn (*const Arguments, *Return) callconv(.c) void, - pub const Unload = fn (self: *Self, alloc: std.mem.Allocator) anyerror![]u8; - pub const CUnload = fn (self: *Self, alloc: *const std.mem.Allocator, ptr: *[*]u8, len: *usize) callconv(.c) bool; - - pub const Reload = fn (self: *Self, alloc: std.mem.Allocator, data: []u8) anyerror!void; - pub const CReload = fn (self: *Self, alloc: *const std.mem.Allocator, ptr: [*]u8, len: usize) callconv(.c) bool; - - pub const Shutdown = fn (self: *Self, alloc: std.mem.Allocator) void; - pub const CShutdown = fn (self: *Self, alloc: *const std.mem.Allocator) callconv(.c) void; - - cstartup: *const CStartup, - creload: *const CReload, - cunload: *const CUnload, - cshutdown: *const CShutdown, - - pub fn startup(mod: @This(), alloc: std.mem.Allocator) !*Self { - return mod.cstartup(&alloc) orelse error.ModuleInitFailed; + pub fn wrap(impl: Function) @This() { + return .{ + .ptr = &struct { + pub fn cimpl(args: *const Arguments, ret: *Return) callconv(.c) void { + ret.* = @call(.auto, impl, args.*); + } + }.cimpl, + }; } - pub fn unload(mod: @This(), self: *Self, alloc: std.mem.Allocator) ![]u8 { - var data: []u8 = undefined; - if (mod.cunload(self, &alloc, &data.ptr, &data.len)) { - return data; - } else { - return error.ModuleUnloadFailed; - } - } - - pub fn reload(mod: @This(), self: *Self, alloc: std.mem.Allocator, data: []u8) !void { - if (mod.creload(self, &alloc, data.ptr, data.len)) {} else { - return error.ModuleReloadFailed; - } - } - - pub fn shutdown(mod: @This(), self: *Self, alloc: std.mem.Allocator) void { - mod.cshutdown(self, &alloc); + pub fn invoke(self: @This(), args: Arguments) Return { + var ret: Return = undefined; + self.ptr(&args, &ret); + return ret; } }; } -pub fn module(Self: type) ModuleType(Self) { - const Module = ModuleType(Self); +pub fn Module(State: type) type { + return extern struct { + pub const Error = std.mem.Allocator.Error; - const impl = struct { - pub const startup: Module.Startup = Self.startup; + pub const StartupError = Error; + pub const UnloadError = Error; + pub const ReloadError = Error; - pub fn cstartup( - alloc: *const std.mem.Allocator, - ) callconv(.c) ?*anyopaque { - const self: *Self = alloc.create(Self) catch |err| { - std.log.err( - "Module {s} startup failed: {!}", - .{ @typeName(Self), err }, - ); - return null; - }; - self.* = startup(alloc.*) catch |err| { - alloc.destroy(self); - std.log.err( - "Module {s} startup failed: {!}", - .{ @typeName(Self), err }, - ); - return null; - }; - return self; - } + const Startup = Slot(fn (std.mem.Allocator) StartupError!*State); + const Unload = Slot(fn (*State, std.mem.Allocator) UnloadError![]u8); + const Reload = Slot(fn (*State, std.mem.Allocator, []u8) ReloadError!void); + const Shutdown = Slot(fn (*State, std.mem.Allocator) void); - pub const unload: Module.Unload = Self.unload; - - pub fn cunload( - self: *Self, - alloc: *const std.mem.Allocator, - ptr: *[*]u8, - len: *usize, - ) callconv(.c) bool { - const data = unload(self, alloc.*) catch |err| { - std.log.err( - "Module {s} unload failed: {!}", - .{ @typeName(Self), err }, - ); - return false; - }; - ptr.* = data.ptr; - len.* = data.len; - return true; - } - - pub const reload: Module.Reload = Self.reload; - - pub fn creload( - self: *Self, - alloc: *const std.mem.Allocator, - ptr: [*]u8, - len: usize, - ) callconv(.c) bool { - reload(self, alloc.*, ptr[0..len]) catch |err| { - std.log.err( - "Module {s} reload failed: {!}", - .{ @typeName(Self), err }, - ); - return false; - }; - return true; - } - - pub const shutdown: Module.Shutdown = Self.shutdown; - - pub fn cshutdown( - self: *Self, - alloc: *const std.mem.Allocator, - ) callconv(.c) void { - shutdown(self, alloc.*); - } + startup: Startup, + unload: Unload, + reload: Reload, + shutdown: Shutdown, }; +} +pub fn module(State: type) Module(State) { return .{ - .cstartup = &impl.cstartup, - .creload = &impl.creload, - .cunload = &impl.cunload, - .cshutdown = &impl.cshutdown, + .startup = Module(State).Startup.wrap(State.startup), + .unload = Module(State).Unload.wrap(State.unload), + .reload = Module(State).Reload.wrap(State.reload), + .shutdown = Module(State).Shutdown.wrap(State.shutdown), }; } + +pub const AnyModule = Module(anyopaque); diff --git a/src/foo.zig b/src/foo.zig index 44b373d..3fca266 100644 --- a/src/foo.zig +++ b/src/foo.zig @@ -5,48 +5,21 @@ const Self = @This(); export const MODULE = core.module(Self); -_unload: std.Thread.ResetEvent = .{}, -looper: ?std.Thread = null, - -pub fn startup(_: std.mem.Allocator) !Self { +pub fn startup(alloc: std.mem.Allocator) !*Self { std.log.debug("!! startup {s} !!", .{@typeName(@This())}); - return .{}; + return try alloc.create(Self); } -pub fn reload(self: *Self, alloc: std.mem.Allocator, _: []u8) !void { - std.log.debug("!! reload {s} !!", .{@typeName(@This())}); - self._unload.reset(); - self.looper = try std.Thread.spawn( - .{ .allocator = alloc }, - _loop, - .{self}, - ); - errdefer { - self._unload.set(); - self.looper.?.join(); - } -} - -fn _loop(self: *Self) void { - std.log.debug("START", .{}); - for (0..10) |i| { - std.log.debug("LOOP {d}", .{i}); - self._unload.timedWait(std.time.ns_per_s) catch continue; - std.log.debug("HALT", .{}); - break; - } -} - -pub fn unload(self: *Self, _: std.mem.Allocator) ![]u8 { +pub fn unload(_: *Self, _: std.mem.Allocator) ![]u8 { std.log.debug("!! unload {s} !!", .{@typeName(@This())}); - if (self.looper) |looper| { - self._unload.set(); - looper.join(); - self.looper = null; - } return &.{}; } -pub fn shutdown(_: *Self, _: std.mem.Allocator) void { - std.log.debug("!! shutdown {s} !!", .{@typeName(@This())}); +pub fn reload(_: *Self, _: std.mem.Allocator, _: []u8) !void { + std.log.debug("!! reload {s} !!", .{@typeName(@This())}); +} + +pub fn shutdown(self: *Self, alloc: std.mem.Allocator) void { + std.log.debug("!! shutdown {s} !!", .{@typeName(@This())}); + alloc.destroy(self); } diff --git a/src/main.zig b/src/main.zig index 8d908e9..c492e83 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,7 +5,7 @@ const core = @import("core.zig"); const ModLib = struct { path: []const u8, lib: std.DynLib, - mod: *core.ModuleType(anyopaque), + mod: *core.Module(anyopaque), fn open(realpath: []const u8, version: u32, alloc: std.mem.Allocator) !ModLib { const basename = std.fs.path.basename(realpath); @@ -27,7 +27,7 @@ const ModLib = struct { var lib = try std.DynLib.open(path); errdefer lib.close(); - const mod = lib.lookup(*core.ModuleType(anyopaque), "MODULE") orelse + const mod = lib.lookup(*core.Module(anyopaque), "MODULE") orelse return error.MissingModuleDefinition; return .{ @@ -71,11 +71,11 @@ const DynamicModule = struct { self.curr = try ModLib.open(realpath, self.next, alloc); errdefer self.curr.close(alloc); - self.state = try self.curr.mod.startup(self.gpa.allocator()); - errdefer self.curr.mod.shutdown(self.state, self.gpa.allocator()); + self.state = try self.curr.mod.startup.invoke(.{self.gpa.allocator()}); + errdefer self.curr.mod.shutdown.invoke(.{ self.state, self.gpa.allocator() }); // todo deserialize from disk. - try self.curr.mod.reload(self.state, self.gpa.allocator(), &.{}); + try self.curr.mod.reload.invoke(.{ self.state, self.gpa.allocator(), &.{} }); self.next += 1; @@ -86,11 +86,11 @@ const DynamicModule = struct { var next = try ModLib.open(self.realpath, self.next, alloc); errdefer next.close(alloc); - const data = try self.curr.mod.unload(self.state, self.gpa.allocator()); - errdefer self.curr.mod.reload(self.state, self.gpa.allocator(), data) catch + const data = try self.curr.mod.unload.invoke(.{ self.state, self.gpa.allocator() }); + errdefer self.curr.mod.reload.invoke(.{ self.state, self.gpa.allocator(), data }) catch std.debug.panic("Failed to rollback to {s}", .{self.curr.path}); - try next.mod.reload(self.state, self.gpa.allocator(), data); + try next.mod.reload.invoke(.{ self.state, self.gpa.allocator(), data }); self.curr.close(alloc); self.next += 1; @@ -98,12 +98,12 @@ const DynamicModule = struct { } pub fn deinit(self: *DynamicModule, alloc: std.mem.Allocator) void { - const data = self.curr.mod.unload(self.state, self.gpa.allocator()) catch + const data = self.curr.mod.unload.invoke(.{ self.state, self.gpa.allocator() }) catch std.debug.panic("Failed to unload {s}", .{self.curr.path}); _ = data; // todo serialize to disk. - self.curr.mod.shutdown(self.state, self.gpa.allocator()); + self.curr.mod.shutdown.invoke(.{ self.state, self.gpa.allocator() }); self.curr.close(alloc);