Merge pull request #1641 from lightpanda-io/contexts_no_realloc

Store Env.Contexts in an [fixed-length] array.
This commit is contained in:
Karl Seguin
2026-02-25 07:30:42 +08:00
committed by GitHub
3 changed files with 45 additions and 16 deletions

View File

@@ -87,7 +87,7 @@ pub fn closeSession(self: *Browser) void {
} }
} }
pub fn runMicrotasks(self: *const Browser) void { pub fn runMicrotasks(self: *Browser) void {
self.env.runMicrotasks(); self.env.runMicrotasks();
} }

View File

@@ -62,7 +62,8 @@ platform: *const Platform,
// the global isolate // the global isolate
isolate: js.Isolate, isolate: js.Isolate,
contexts: std.ArrayList(*js.Context), contexts: [64]*Context,
context_count: usize,
// just kept around because we need to free it on deinit // just kept around because we need to free it on deinit
isolate_params: *v8.CreateParams, isolate_params: *v8.CreateParams,
@@ -85,6 +86,8 @@ inspector: ?*Inspector,
// which an be created once per isolaet. // which an be created once per isolaet.
private_symbols: PrivateSymbols, private_symbols: PrivateSymbols,
microtask_queues_are_running: bool,
pub const InitOpts = struct { pub const InitOpts = struct {
with_inspector: bool = false, with_inspector: bool = false,
}; };
@@ -203,7 +206,8 @@ pub fn init(app: *App, opts: InitOpts) !Env {
return .{ return .{
.app = app, .app = app,
.context_id = 0, .context_id = 0,
.contexts = .empty, .contexts = undefined,
.context_count = 0,
.isolate = isolate, .isolate = isolate,
.platform = &app.platform, .platform = &app.platform,
.templates = templates, .templates = templates,
@@ -211,15 +215,16 @@ pub fn init(app: *App, opts: InitOpts) !Env {
.inspector = inspector, .inspector = inspector,
.global_template = global_eternal, .global_template = global_eternal,
.private_symbols = private_symbols, .private_symbols = private_symbols,
.microtask_queues_are_running = false,
.eternal_function_templates = eternal_function_templates, .eternal_function_templates = eternal_function_templates,
}; };
} }
pub fn deinit(self: *Env) void { pub fn deinit(self: *Env) void {
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
std.debug.assert(self.contexts.items.len == 0); std.debug.assert(self.context_count == 0);
} }
for (self.contexts.items) |ctx| { for (self.contexts[0..self.context_count]) |ctx| {
ctx.deinit(); ctx.deinit();
} }
@@ -228,8 +233,6 @@ pub fn deinit(self: *Env) void {
i.deinit(allocator); i.deinit(allocator);
} }
self.contexts.deinit(allocator);
allocator.free(self.templates); allocator.free(self.templates);
allocator.free(self.eternal_function_templates); allocator.free(self.eternal_function_templates);
self.private_symbols.deinit(); self.private_symbols.deinit();
@@ -312,14 +315,22 @@ pub fn createContext(self: *Env, page: *Page) !*Context {
// a v8 context, we can get our context out // a v8 context, we can get our context out
v8.v8__Context__SetAlignedPointerInEmbedderData(v8_context, 1, @ptrCast(context)); v8.v8__Context__SetAlignedPointerInEmbedderData(v8_context, 1, @ptrCast(context));
try self.contexts.append(self.app.allocator, context); const count = self.context_count;
if (count >= self.contexts.len) {
return error.TooManyContexts;
}
self.contexts[count] = context;
self.context_count = count + 1;
return context; return context;
} }
pub fn destroyContext(self: *Env, context: *Context) void { pub fn destroyContext(self: *Env, context: *Context) void {
for (self.contexts.items, 0..) |ctx, i| { for (self.contexts[0..self.context_count], 0..) |ctx, i| {
if (ctx == context) { if (ctx == context) {
_ = self.contexts.swapRemove(i); // Swap with last element and decrement count
self.context_count -= 1;
self.contexts[i] = self.contexts[self.context_count];
break; break;
} }
} else { } else {
@@ -339,16 +350,24 @@ pub fn destroyContext(self: *Env, context: *Context) void {
context.deinit(); context.deinit();
} }
pub fn runMicrotasks(self: *const Env) void { pub fn runMicrotasks(self: *Env) void {
const v8_isolate = self.isolate.handle; if (self.microtask_queues_are_running == false) {
for (self.contexts.items) |ctx| { const v8_isolate = self.isolate.handle;
v8.v8__MicrotaskQueue__PerformCheckpoint(ctx.microtask_queue, v8_isolate);
self.microtask_queues_are_running = true;
defer self.microtask_queues_are_running = false;
var i: usize = 0;
while (i < self.context_count) : (i += 1) {
const ctx = self.contexts[i];
v8.v8__MicrotaskQueue__PerformCheckpoint(ctx.microtask_queue, v8_isolate);
}
} }
} }
pub fn runMacrotasks(self: *Env) !?u64 { pub fn runMacrotasks(self: *Env) !?u64 {
var ms_to_next_task: ?u64 = null; var ms_to_next_task: ?u64 = null;
for (self.contexts.items) |ctx| { for (self.contexts[0..self.context_count]) |ctx| {
if (comptime builtin.is_test == false) { if (comptime builtin.is_test == false) {
// I hate this comptime check as much as you do. But we have tests // I hate this comptime check as much as you do. But we have tests
// which rely on short execution before shutdown. In real world, it's // which rely on short execution before shutdown. In real world, it's

View File

@@ -713,7 +713,17 @@ pub fn normalize(self: *Node, page: *Page) !void {
return self._normalize(page.call_arena, &buffer, page); return self._normalize(page.call_arena, &buffer, page);
} }
pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) error{ OutOfMemory, StringTooLarge, NotSupported, NotImplemented, InvalidCharacterError, CloneError, IFrameLoadError }!*Node { const CloneError = error{
OutOfMemory,
StringTooLarge,
NotSupported,
NotImplemented,
InvalidCharacterError,
CloneError,
IFrameLoadError,
TooManyContexts,
};
pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) CloneError!*Node {
const deep = deep_ orelse false; const deep = deep_ orelse false;
switch (self._type) { switch (self._type) {
.cdata => |cd| { .cdata => |cd| {