diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 8ea3c9bc..b2840e3c 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -91,6 +91,11 @@ private_symbols: PrivateSymbols, microtask_queues_are_running: bool, +// Serializes V8 calls that race with TerminateExecution (which can fire from +// the sighandler thread). Without this, a terminate landing between the +// IsExecutionTerminating check and PerformCheckpoint trips a V8 debug assert. +terminate_mutex: std.Thread.Mutex = .{}, + pub const InitOpts = struct { with_inspector: bool = false, }; @@ -360,6 +365,9 @@ pub fn destroyContext(self: *Env, context: *Context) void { pub fn runMicrotasks(self: *Env) void { if (self.microtask_queues_are_running == false) { + self.terminate_mutex.lock(); + defer self.terminate_mutex.unlock(); + const v8_isolate = self.isolate.handle; if (v8.v8__Isolate__IsExecutionTerminating(v8_isolate)) { @@ -489,14 +497,18 @@ pub fn dumpMemoryStats(self: *Env) void { , .{ stats.total_heap_size, stats.total_heap_size_executable, stats.total_physical_size, stats.total_available_size, stats.used_heap_size, stats.heap_size_limit, stats.malloced_memory, stats.external_memory, stats.peak_malloced_memory, stats.number_of_native_contexts, stats.number_of_detached_contexts, stats.total_global_handles_size, stats.used_global_handles_size, stats.does_zap_garbage }); } -pub fn terminate(self: *const Env) void { +pub fn terminate(self: *Env) void { + self.terminate_mutex.lock(); + defer self.terminate_mutex.unlock(); v8.v8__Isolate__TerminateExecution(self.isolate.handle); } /// Clears a pending termination so V8 calls (e.g. those made during cleanup) /// don't keep tripping over the terminating-state asserts. Safe to call /// unconditionally; a no-op if termination wasn't pending. -pub fn cancelTerminate(self: *const Env) void { +pub fn cancelTerminate(self: *Env) void { + self.terminate_mutex.lock(); + defer self.terminate_mutex.unlock(); v8.v8__Isolate__CancelTerminateExecution(self.isolate.handle); }