unify "slot" construct
This commit is contained in:
@@ -5,9 +5,9 @@ const Self = @This();
|
|||||||
|
|
||||||
export const MODULE = core.module(Self);
|
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())});
|
std.log.debug("!! startup {s} !!", .{@typeName(@This())});
|
||||||
return .{};
|
return try alloc.create(Self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unload(_: *Self, _: std.mem.Allocator) ![]u8 {
|
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())});
|
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())});
|
std.log.debug("!! shutdown {s} !!", .{@typeName(@This())});
|
||||||
|
alloc.destroy(self);
|
||||||
}
|
}
|
||||||
|
205
src/core.zig
205
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
|
// 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.
|
// 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 {
|
return extern struct {
|
||||||
pub const Startup = fn (alloc: std.mem.Allocator) anyerror!Self;
|
ptr: *const fn (*const Arguments, *Return) callconv(.c) void,
|
||||||
pub const CStartup = fn (alloc: *const std.mem.Allocator) callconv(.c) ?*anyopaque;
|
|
||||||
|
|
||||||
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 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 module(Self: type) ModuleType(Self) {
|
|
||||||
const Module = ModuleType(Self);
|
|
||||||
|
|
||||||
const impl = struct {
|
|
||||||
pub const startup: Module.Startup = Self.startup;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.*);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
pub fn wrap(impl: Function) @This() {
|
||||||
return .{
|
return .{
|
||||||
.cstartup = &impl.cstartup,
|
.ptr = &struct {
|
||||||
.creload = &impl.creload,
|
pub fn cimpl(args: *const Arguments, ret: *Return) callconv(.c) void {
|
||||||
.cunload = &impl.cunload,
|
ret.* = @call(.auto, impl, args.*);
|
||||||
.cshutdown = &impl.cshutdown,
|
}
|
||||||
|
}.cimpl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn invoke(self: @This(), args: Arguments) Return {
|
||||||
|
var ret: Return = undefined;
|
||||||
|
self.ptr(&args, &ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Module(State: type) type {
|
||||||
|
return extern struct {
|
||||||
|
pub const Error = std.mem.Allocator.Error;
|
||||||
|
|
||||||
|
pub const StartupError = Error;
|
||||||
|
pub const UnloadError = Error;
|
||||||
|
pub const ReloadError = Error;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
startup: Startup,
|
||||||
|
unload: Unload,
|
||||||
|
reload: Reload,
|
||||||
|
shutdown: Shutdown,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn module(State: type) Module(State) {
|
||||||
|
return .{
|
||||||
|
.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);
|
||||||
|
47
src/foo.zig
47
src/foo.zig
@@ -5,48 +5,21 @@ const Self = @This();
|
|||||||
|
|
||||||
export const MODULE = core.module(Self);
|
export const MODULE = core.module(Self);
|
||||||
|
|
||||||
_unload: std.Thread.ResetEvent = .{},
|
pub fn startup(alloc: std.mem.Allocator) !*Self {
|
||||||
looper: ?std.Thread = null,
|
|
||||||
|
|
||||||
pub fn startup(_: std.mem.Allocator) !Self {
|
|
||||||
std.log.debug("!! startup {s} !!", .{@typeName(@This())});
|
std.log.debug("!! startup {s} !!", .{@typeName(@This())});
|
||||||
return .{};
|
return try alloc.create(Self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload(self: *Self, alloc: std.mem.Allocator, _: []u8) !void {
|
pub fn unload(_: *Self, _: std.mem.Allocator) ![]u8 {
|
||||||
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 {
|
|
||||||
std.log.debug("!! unload {s} !!", .{@typeName(@This())});
|
std.log.debug("!! unload {s} !!", .{@typeName(@This())});
|
||||||
if (self.looper) |looper| {
|
|
||||||
self._unload.set();
|
|
||||||
looper.join();
|
|
||||||
self.looper = null;
|
|
||||||
}
|
|
||||||
return &.{};
|
return &.{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shutdown(_: *Self, _: std.mem.Allocator) void {
|
pub fn reload(_: *Self, _: std.mem.Allocator, _: []u8) !void {
|
||||||
std.log.debug("!! shutdown {s} !!", .{@typeName(@This())});
|
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);
|
||||||
}
|
}
|
||||||
|
20
src/main.zig
20
src/main.zig
@@ -5,7 +5,7 @@ const core = @import("core.zig");
|
|||||||
const ModLib = struct {
|
const ModLib = struct {
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
lib: std.DynLib,
|
lib: std.DynLib,
|
||||||
mod: *core.ModuleType(anyopaque),
|
mod: *core.Module(anyopaque),
|
||||||
|
|
||||||
fn open(realpath: []const u8, version: u32, alloc: std.mem.Allocator) !ModLib {
|
fn open(realpath: []const u8, version: u32, alloc: std.mem.Allocator) !ModLib {
|
||||||
const basename = std.fs.path.basename(realpath);
|
const basename = std.fs.path.basename(realpath);
|
||||||
@@ -27,7 +27,7 @@ const ModLib = struct {
|
|||||||
var lib = try std.DynLib.open(path);
|
var lib = try std.DynLib.open(path);
|
||||||
errdefer lib.close();
|
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 error.MissingModuleDefinition;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@@ -71,11 +71,11 @@ const DynamicModule = struct {
|
|||||||
self.curr = try ModLib.open(realpath, self.next, alloc);
|
self.curr = try ModLib.open(realpath, self.next, alloc);
|
||||||
errdefer self.curr.close(alloc);
|
errdefer self.curr.close(alloc);
|
||||||
|
|
||||||
self.state = try self.curr.mod.startup(self.gpa.allocator());
|
self.state = try self.curr.mod.startup.invoke(.{self.gpa.allocator()});
|
||||||
errdefer self.curr.mod.shutdown(self.state, self.gpa.allocator());
|
errdefer self.curr.mod.shutdown.invoke(.{ self.state, self.gpa.allocator() });
|
||||||
|
|
||||||
// todo deserialize from disk.
|
// 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;
|
self.next += 1;
|
||||||
|
|
||||||
@@ -86,11 +86,11 @@ const DynamicModule = struct {
|
|||||||
var next = try ModLib.open(self.realpath, self.next, alloc);
|
var next = try ModLib.open(self.realpath, self.next, alloc);
|
||||||
errdefer next.close(alloc);
|
errdefer next.close(alloc);
|
||||||
|
|
||||||
const data = try self.curr.mod.unload(self.state, self.gpa.allocator());
|
const data = try self.curr.mod.unload.invoke(.{ self.state, self.gpa.allocator() });
|
||||||
errdefer self.curr.mod.reload(self.state, self.gpa.allocator(), data) catch
|
errdefer self.curr.mod.reload.invoke(.{ self.state, self.gpa.allocator(), data }) catch
|
||||||
std.debug.panic("Failed to rollback to {s}", .{self.curr.path});
|
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.curr.close(alloc);
|
||||||
self.next += 1;
|
self.next += 1;
|
||||||
@@ -98,12 +98,12 @@ const DynamicModule = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *DynamicModule, alloc: std.mem.Allocator) void {
|
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});
|
std.debug.panic("Failed to unload {s}", .{self.curr.path});
|
||||||
|
|
||||||
_ = data; // todo serialize to disk.
|
_ = 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);
|
self.curr.close(alloc);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user