diff --git a/src/js/export_module.zig b/src/js/export_module.zig index 1b9a3ae..0a4b4a2 100644 --- a/src/js/export_module.zig +++ b/src/js/export_module.zig @@ -34,7 +34,8 @@ const class_runtime = @import("class_runtime.zig"); /// /// The DSL internally manages an atomic refcount for module instances across /// different N-API environments, and uses the same env lifecycle to retain a -/// shared `js.io()` handle for the addon. +/// shared `js.io()` handle for the addon. Modules with lifecycle hooks also +/// serialize initialization and cleanup across environments. /// /// Usage Examples: /// ```zig @@ -66,6 +67,7 @@ pub fn exportModule(comptime Module: type, comptime options: anytype) void { const State = struct { var env_refcount: std.atomic.Value(u32) = std.atomic.Value(u32).init(0); + var lifecycle_mutex: std.Io.Mutex = .init; // addEnvCleanupHook requires a non-null *Data pointer. const CleanupData = struct { @@ -75,6 +77,10 @@ pub fn exportModule(comptime Module: type, comptime options: anytype) void { fn cleanupHook(_: *CleanupData) void { if (has_lifecycle) { + const io = io_context.io(); + lifecycle_mutex.lockUncancelable(io); + defer lifecycle_mutex.unlock(io); + const prev = env_refcount.fetchSub(1, .acq_rel); const new_refcount = prev - 1; if (has_cleanup) { @@ -96,6 +102,12 @@ pub fn exportModule(comptime Module: type, comptime options: anytype) void { io_context.release(); }; + const io = io_context.io(); + if (has_lifecycle) { + State.lifecycle_mutex.lockUncancelable(io); + } + defer if (has_lifecycle) State.lifecycle_mutex.unlock(io); + var prev_refcount: u32 = 0; if (has_lifecycle) { prev_refcount = State.env_refcount.fetchAdd(1, .monotonic);