Mutex for Isolate.Terminate

Improvement to https://github.com/lightpanda-io/browser/pull/2246

In that PR, the check for `v8__Isolate__IsExecutionTerminating` guarded
runMicrotasks from running when the execution was terminated, but what if
termination happens immediately after?  This adds a mutex to ensure that the
isolate cannot be terminated while microtasks are being processed.
This commit is contained in:
Karl Seguin
2026-04-27 11:10:38 +08:00
parent a578f4d6ad
commit 6e729131ed

View File

@@ -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);
}