Fix Context's call_arena

The Context's call_arena should be based on the source, e.g. the IsolateWorld
or the Page, not always the page. There's no rule that says all Contexts have
to be a subset of the Page, and thus some might live longer and by doing so
outlive the page_arena.

Also, on context cleanup, isolate worlds now cleanup their identity.
This commit is contained in:
Karl Seguin
2026-03-20 16:50:03 +08:00
parent f70865e174
commit 88681b1fdb
5 changed files with 15 additions and 3 deletions

View File

@@ -112,7 +112,6 @@ pub fn acquire(self: *ArenaPool, dbg: DebugInfo) !Allocator {
} }
gop.value_ptr.* += 1; gop.value_ptr.* += 1;
} }
return entry.arena.allocator(); return entry.arena.allocator();
} }

View File

@@ -305,6 +305,7 @@ pub fn init(self: *Page, frame_id: u32, session: *Session, parent: ?*Page) !void
self.js = try browser.env.createContext(self, .{ self.js = try browser.env.createContext(self, .{
.identity = &session.identity, .identity = &session.identity,
.identity_arena = session.page_arena, .identity_arena = session.page_arena,
.call_arena = self.call_arena,
}); });
errdefer self.js.deinit(); errdefer self.js.deinit();

View File

@@ -63,7 +63,9 @@ templates: []*const v8.FunctionTemplate,
// Arena for the lifetime of the context // Arena for the lifetime of the context
arena: Allocator, arena: Allocator,
// The page.call_arena // The call_arena for this context. For main world contexts this is
// page.call_arena. For isolated world contexts this is a separate arena
// owned by the IsolatedWorld.
call_arena: Allocator, call_arena: Allocator,
// Because calls can be nested (i.e.a function calling a callback), // Because calls can be nested (i.e.a function calling a callback),

View File

@@ -258,6 +258,7 @@ pub fn deinit(self: *Env) void {
pub const ContextParams = struct { pub const ContextParams = struct {
identity: *js.Identity, identity: *js.Identity,
identity_arena: Allocator, identity_arena: Allocator,
call_arena: Allocator,
debug_name: []const u8 = "Context", debug_name: []const u8 = "Context",
}; };
@@ -325,7 +326,7 @@ pub fn createContext(self: *Env, page: *Page, params: ContextParams) !*Context {
.arena = context_arena, .arena = context_arena,
.handle = context_global, .handle = context_global,
.templates = self.templates, .templates = self.templates,
.call_arena = page.call_arena, .call_arena = params.call_arena,
.microtask_queue = microtask_queue, .microtask_queue = microtask_queue,
.script_manager = &page._script_manager, .script_manager = &page._script_manager,
.scheduler = .init(context_arena), .scheduler = .init(context_arena),

View File

@@ -492,9 +492,13 @@ pub fn BrowserContext(comptime CDP_T: type) type {
const arena = try browser.arena_pool.acquire(.{ .debug = "IsolatedWorld" }); const arena = try browser.arena_pool.acquire(.{ .debug = "IsolatedWorld" });
errdefer browser.arena_pool.release(arena); errdefer browser.arena_pool.release(arena);
const call_arena = try browser.arena_pool.acquire(.{ .debug = "IsolatedWorld.call_arena" });
errdefer browser.arena_pool.release(call_arena);
const world = try arena.create(IsolatedWorld); const world = try arena.create(IsolatedWorld);
world.* = .{ world.* = .{
.arena = arena, .arena = arena,
.call_arena = call_arena,
.context = null, .context = null,
.browser = browser, .browser = browser,
.name = try arena.dupe(u8, world_name), .name = try arena.dupe(u8, world_name),
@@ -745,6 +749,7 @@ pub fn BrowserContext(comptime CDP_T: type) type {
/// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts. /// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts.
const IsolatedWorld = struct { const IsolatedWorld = struct {
arena: Allocator, arena: Allocator,
call_arena: Allocator,
browser: *Browser, browser: *Browser,
name: []const u8, name: []const u8,
context: ?*js.Context = null, context: ?*js.Context = null,
@@ -757,6 +762,7 @@ const IsolatedWorld = struct {
pub fn deinit(self: *IsolatedWorld) void { pub fn deinit(self: *IsolatedWorld) void {
self.removeContext() catch {}; self.removeContext() catch {};
self.identity.deinit(); self.identity.deinit();
self.browser.arena_pool.release(self.call_arena);
self.browser.arena_pool.release(self.arena); self.browser.arena_pool.release(self.arena);
} }
@@ -764,6 +770,8 @@ const IsolatedWorld = struct {
const ctx = self.context orelse return error.NoIsolatedContextToRemove; const ctx = self.context orelse return error.NoIsolatedContextToRemove;
self.browser.env.destroyContext(ctx); self.browser.env.destroyContext(ctx);
self.context = null; self.context = null;
self.identity.deinit();
self.identity = .{};
} }
// The isolate world must share at least some of the state with the related page, specifically the DocumentHTML // The isolate world must share at least some of the state with the related page, specifically the DocumentHTML
@@ -776,6 +784,7 @@ const IsolatedWorld = struct {
const ctx = try self.browser.env.createContext(page, .{ const ctx = try self.browser.env.createContext(page, .{
.identity = &self.identity, .identity = &self.identity,
.identity_arena = self.arena, .identity_arena = self.arena,
.call_arena = self.call_arena,
.debug_name = "IsolatedContext", .debug_name = "IsolatedContext",
}); });
self.context = ctx; self.context = ctx;