mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
Merge pull request #1445 from lightpanda-io/schedule_task_finalizer
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Allow [schedule] tasks to have finalizers
This commit is contained in:
@@ -234,6 +234,10 @@ pub fn deinit(self: *Page) void {
|
||||
// stats.print(&stream) catch unreachable;
|
||||
}
|
||||
|
||||
// This can release JS objects, so we need to do this while the js.Context
|
||||
// is still around.
|
||||
self.scheduler.deinit();
|
||||
|
||||
{
|
||||
// some MicroTasks might be referencing the page, we need to drain it while
|
||||
// the page still exists
|
||||
@@ -266,6 +270,8 @@ fn reset(self: *Page, comptime initializing: bool) !void {
|
||||
const browser = self._session.browser;
|
||||
|
||||
if (comptime initializing == false) {
|
||||
self.scheduler.deinit();
|
||||
|
||||
browser.env.destroyContext(self.js);
|
||||
|
||||
// removing a context can trigger finalizers, so we can only check for
|
||||
@@ -281,7 +287,6 @@ fn reset(self: *Page, comptime initializing: bool) !void {
|
||||
// We force a garbage collection between page navigations to keep v8
|
||||
// memory usage as low as possible.
|
||||
browser.env.memoryPressureNotification(.moderate);
|
||||
|
||||
self._script_manager.shutdown = true;
|
||||
browser.http_client.abort();
|
||||
self._script_manager.deinit();
|
||||
|
||||
@@ -47,9 +47,15 @@ pub fn init(allocator: std.mem.Allocator) Scheduler {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Scheduler) void {
|
||||
finalizeTasks(&self.low_priority);
|
||||
finalizeTasks(&self.high_priority);
|
||||
}
|
||||
|
||||
const AddOpts = struct {
|
||||
name: []const u8 = "",
|
||||
low_priority: bool = false,
|
||||
finalizer: ?Finalizer = null,
|
||||
};
|
||||
pub fn add(self: *Scheduler, ctx: *anyopaque, cb: Callback, run_in_ms: u32, opts: AddOpts) !void {
|
||||
if (comptime IS_DEBUG) {
|
||||
@@ -63,6 +69,7 @@ pub fn add(self: *Scheduler, ctx: *anyopaque, cb: Callback, run_in_ms: u32, opts
|
||||
.callback = cb,
|
||||
.sequence = seq,
|
||||
.name = opts.name,
|
||||
.finalizer = opts.finalizer,
|
||||
.run_at = milliTimestamp(.monotonic) + run_in_ms,
|
||||
});
|
||||
}
|
||||
@@ -105,12 +112,23 @@ fn runQueue(self: *Scheduler, queue: *Queue) !?u64 {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn finalizeTasks(queue: *Queue) void {
|
||||
var it = queue.iterator();
|
||||
while (it.next()) |t| {
|
||||
if (t.finalizer) |func| {
|
||||
func(t.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Task = struct {
|
||||
run_at: u64,
|
||||
sequence: u64,
|
||||
ctx: *anyopaque,
|
||||
name: []const u8,
|
||||
callback: Callback,
|
||||
finalizer: ?Finalizer,
|
||||
};
|
||||
|
||||
const Callback = *const fn (ctx: *anyopaque) anyerror!?u32;
|
||||
const Finalizer = *const fn (ctx: *anyopaque) void;
|
||||
|
||||
@@ -44,6 +44,8 @@ const CSSStyleProperties = @import("css/CSSStyleProperties.zig");
|
||||
const CustomElementRegistry = @import("CustomElementRegistry.zig");
|
||||
const Selection = @import("Selection.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Window = @This();
|
||||
|
||||
_proto: *EventTarget,
|
||||
@@ -353,18 +355,21 @@ pub fn postMessage(self: *Window, message: js.Value.Global, target_origin: ?[]co
|
||||
_ = target_origin;
|
||||
|
||||
// postMessage queues a task (not a microtask), so use the scheduler
|
||||
const origin = try self._location.getOrigin(page);
|
||||
const callback = try page._factory.create(PostMessageCallback{
|
||||
.window = self,
|
||||
.message = message,
|
||||
.origin = try page.arena.dupe(u8, origin),
|
||||
.page = page,
|
||||
});
|
||||
errdefer page._factory.destroy(callback);
|
||||
const arena = try page.getArena(.{ .debug = "Window.schedule" });
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const origin = try self._location.getOrigin(page);
|
||||
const callback = try arena.create(PostMessageCallback);
|
||||
callback.* = .{
|
||||
.page = page,
|
||||
.arena = arena,
|
||||
.message = message,
|
||||
.origin = try arena.dupe(u8, origin),
|
||||
};
|
||||
try page.scheduler.add(callback, PostMessageCallback.run, 0, .{
|
||||
.name = "postMessage",
|
||||
.low_priority = false,
|
||||
.finalizer = PostMessageCallback.cancelled,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -508,13 +513,16 @@ fn scheduleCallback(self: *Window, cb: js.Function.Temp, delay_ms: u32, opts: Sc
|
||||
return error.TooManyTimeout;
|
||||
}
|
||||
|
||||
const arena = try page.getArena(.{ .debug = "Window.schedule" });
|
||||
errdefer page.releaseArena(arena);
|
||||
|
||||
const timer_id = self._timer_id +% 1;
|
||||
self._timer_id = timer_id;
|
||||
|
||||
const params = opts.params;
|
||||
var persisted_params: []js.Value.Temp = &.{};
|
||||
if (params.len > 0) {
|
||||
persisted_params = try page.arena.dupe(js.Value.Temp, params);
|
||||
persisted_params = try arena.dupe(js.Value.Temp, params);
|
||||
}
|
||||
|
||||
const gop = try self._timers.getOrPut(page.arena, timer_id);
|
||||
@@ -524,21 +532,23 @@ fn scheduleCallback(self: *Window, cb: js.Function.Temp, delay_ms: u32, opts: Sc
|
||||
}
|
||||
errdefer _ = self._timers.remove(timer_id);
|
||||
|
||||
const callback = try page._factory.create(ScheduleCallback{
|
||||
const callback = try arena.create(ScheduleCallback);
|
||||
callback.* = .{
|
||||
.cb = cb,
|
||||
.page = page,
|
||||
.arena = arena,
|
||||
.mode = opts.mode,
|
||||
.name = opts.name,
|
||||
.timer_id = timer_id,
|
||||
.params = persisted_params,
|
||||
.repeat_ms = if (opts.repeat) if (delay_ms == 0) 1 else delay_ms else null,
|
||||
});
|
||||
};
|
||||
gop.value_ptr.* = callback;
|
||||
errdefer page._factory.destroy(callback);
|
||||
|
||||
try page.scheduler.add(callback, ScheduleCallback.run, delay_ms, .{
|
||||
.name = opts.name,
|
||||
.low_priority = opts.low_priority,
|
||||
.finalizer = ScheduleCallback.cancelled,
|
||||
});
|
||||
|
||||
return timer_id;
|
||||
@@ -556,13 +566,11 @@ const ScheduleCallback = struct {
|
||||
|
||||
cb: js.Function.Temp,
|
||||
|
||||
page: *Page,
|
||||
|
||||
params: []const js.Value.Temp,
|
||||
|
||||
removed: bool = false,
|
||||
|
||||
mode: Mode,
|
||||
page: *Page,
|
||||
arena: Allocator,
|
||||
removed: bool = false,
|
||||
params: []const js.Value.Temp,
|
||||
|
||||
const Mode = enum {
|
||||
idle,
|
||||
@@ -570,19 +578,26 @@ const ScheduleCallback = struct {
|
||||
animation_frame,
|
||||
};
|
||||
|
||||
fn cancelled(ctx: *anyopaque) void {
|
||||
var self: *ScheduleCallback = @ptrCast(@alignCast(ctx));
|
||||
self.deinit();
|
||||
}
|
||||
|
||||
fn deinit(self: *ScheduleCallback) void {
|
||||
self.page.js.release(self.cb);
|
||||
for (self.params) |param| {
|
||||
self.page.js.release(param);
|
||||
}
|
||||
self.page._factory.destroy(self);
|
||||
self.page.releaseArena(self.arena);
|
||||
}
|
||||
|
||||
fn run(ctx: *anyopaque) !?u32 {
|
||||
const self: *ScheduleCallback = @ptrCast(@alignCast(ctx));
|
||||
const page = self.page;
|
||||
const window = page.window;
|
||||
|
||||
if (self.removed) {
|
||||
_ = page.window._timers.remove(self.timer_id);
|
||||
_ = window._timers.remove(self.timer_id);
|
||||
self.deinit();
|
||||
return null;
|
||||
}
|
||||
@@ -599,7 +614,7 @@ const ScheduleCallback = struct {
|
||||
};
|
||||
},
|
||||
.animation_frame => {
|
||||
ls.toLocal(self.cb).call(void, .{page.window._performance.now()}) catch |err| {
|
||||
ls.toLocal(self.cb).call(void, .{window._performance.now()}) catch |err| {
|
||||
log.warn(.js, "window.RAF", .{ .name = self.name, .err = err });
|
||||
};
|
||||
},
|
||||
@@ -614,35 +629,43 @@ const ScheduleCallback = struct {
|
||||
return ms;
|
||||
}
|
||||
defer self.deinit();
|
||||
_ = page.window._timers.remove(self.timer_id);
|
||||
_ = window._timers.remove(self.timer_id);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const PostMessageCallback = struct {
|
||||
window: *Window,
|
||||
message: js.Value.Global,
|
||||
origin: []const u8,
|
||||
page: *Page,
|
||||
arena: Allocator,
|
||||
origin: []const u8,
|
||||
message: js.Value.Global,
|
||||
|
||||
fn deinit(self: *PostMessageCallback) void {
|
||||
self.page._factory.destroy(self);
|
||||
self.page.releaseArena(self.arena);
|
||||
}
|
||||
|
||||
fn cancelled(ctx: *anyopaque) void {
|
||||
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
||||
self.page.releaseArena(self.arena);
|
||||
}
|
||||
|
||||
fn run(ctx: *anyopaque) !?u32 {
|
||||
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
||||
defer self.deinit();
|
||||
|
||||
const page = self.page;
|
||||
const window = page.window;
|
||||
|
||||
const message_event = try MessageEvent.initTrusted("message", .{
|
||||
.data = self.message,
|
||||
.origin = self.origin,
|
||||
.source = self.window,
|
||||
.source = window,
|
||||
.bubbles = false,
|
||||
.cancelable = false,
|
||||
}, self.page);
|
||||
}, page);
|
||||
|
||||
const event = message_event.asEvent();
|
||||
try self.page._event_manager.dispatch(self.window.asEventTarget(), event);
|
||||
try page._event_manager.dispatch(window.asEventTarget(), event);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user