mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 06:23:45 +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;
|
// 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
|
// some MicroTasks might be referencing the page, we need to drain it while
|
||||||
// the page still exists
|
// the page still exists
|
||||||
@@ -266,6 +270,8 @@ fn reset(self: *Page, comptime initializing: bool) !void {
|
|||||||
const browser = self._session.browser;
|
const browser = self._session.browser;
|
||||||
|
|
||||||
if (comptime initializing == false) {
|
if (comptime initializing == false) {
|
||||||
|
self.scheduler.deinit();
|
||||||
|
|
||||||
browser.env.destroyContext(self.js);
|
browser.env.destroyContext(self.js);
|
||||||
|
|
||||||
// removing a context can trigger finalizers, so we can only check for
|
// 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
|
// We force a garbage collection between page navigations to keep v8
|
||||||
// memory usage as low as possible.
|
// memory usage as low as possible.
|
||||||
browser.env.memoryPressureNotification(.moderate);
|
browser.env.memoryPressureNotification(.moderate);
|
||||||
|
|
||||||
self._script_manager.shutdown = true;
|
self._script_manager.shutdown = true;
|
||||||
browser.http_client.abort();
|
browser.http_client.abort();
|
||||||
self._script_manager.deinit();
|
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 {
|
const AddOpts = struct {
|
||||||
name: []const u8 = "",
|
name: []const u8 = "",
|
||||||
low_priority: bool = false,
|
low_priority: bool = false,
|
||||||
|
finalizer: ?Finalizer = null,
|
||||||
};
|
};
|
||||||
pub fn add(self: *Scheduler, ctx: *anyopaque, cb: Callback, run_in_ms: u32, opts: AddOpts) !void {
|
pub fn add(self: *Scheduler, ctx: *anyopaque, cb: Callback, run_in_ms: u32, opts: AddOpts) !void {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
@@ -63,6 +69,7 @@ pub fn add(self: *Scheduler, ctx: *anyopaque, cb: Callback, run_in_ms: u32, opts
|
|||||||
.callback = cb,
|
.callback = cb,
|
||||||
.sequence = seq,
|
.sequence = seq,
|
||||||
.name = opts.name,
|
.name = opts.name,
|
||||||
|
.finalizer = opts.finalizer,
|
||||||
.run_at = milliTimestamp(.monotonic) + run_in_ms,
|
.run_at = milliTimestamp(.monotonic) + run_in_ms,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -105,12 +112,23 @@ fn runQueue(self: *Scheduler, queue: *Queue) !?u64 {
|
|||||||
return null;
|
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 {
|
const Task = struct {
|
||||||
run_at: u64,
|
run_at: u64,
|
||||||
sequence: u64,
|
sequence: u64,
|
||||||
ctx: *anyopaque,
|
ctx: *anyopaque,
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
callback: Callback,
|
callback: Callback,
|
||||||
|
finalizer: ?Finalizer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Callback = *const fn (ctx: *anyopaque) anyerror!?u32;
|
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 CustomElementRegistry = @import("CustomElementRegistry.zig");
|
||||||
const Selection = @import("Selection.zig");
|
const Selection = @import("Selection.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const Window = @This();
|
const Window = @This();
|
||||||
|
|
||||||
_proto: *EventTarget,
|
_proto: *EventTarget,
|
||||||
@@ -353,18 +355,21 @@ pub fn postMessage(self: *Window, message: js.Value.Global, target_origin: ?[]co
|
|||||||
_ = target_origin;
|
_ = target_origin;
|
||||||
|
|
||||||
// postMessage queues a task (not a microtask), so use the scheduler
|
// postMessage queues a task (not a microtask), so use the scheduler
|
||||||
const origin = try self._location.getOrigin(page);
|
const arena = try page.getArena(.{ .debug = "Window.schedule" });
|
||||||
const callback = try page._factory.create(PostMessageCallback{
|
errdefer page.releaseArena(arena);
|
||||||
.window = self,
|
|
||||||
.message = message,
|
|
||||||
.origin = try page.arena.dupe(u8, origin),
|
|
||||||
.page = page,
|
|
||||||
});
|
|
||||||
errdefer page._factory.destroy(callback);
|
|
||||||
|
|
||||||
|
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, .{
|
try page.scheduler.add(callback, PostMessageCallback.run, 0, .{
|
||||||
.name = "postMessage",
|
.name = "postMessage",
|
||||||
.low_priority = false,
|
.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;
|
return error.TooManyTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const arena = try page.getArena(.{ .debug = "Window.schedule" });
|
||||||
|
errdefer page.releaseArena(arena);
|
||||||
|
|
||||||
const timer_id = self._timer_id +% 1;
|
const timer_id = self._timer_id +% 1;
|
||||||
self._timer_id = timer_id;
|
self._timer_id = timer_id;
|
||||||
|
|
||||||
const params = opts.params;
|
const params = opts.params;
|
||||||
var persisted_params: []js.Value.Temp = &.{};
|
var persisted_params: []js.Value.Temp = &.{};
|
||||||
if (params.len > 0) {
|
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);
|
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);
|
errdefer _ = self._timers.remove(timer_id);
|
||||||
|
|
||||||
const callback = try page._factory.create(ScheduleCallback{
|
const callback = try arena.create(ScheduleCallback);
|
||||||
|
callback.* = .{
|
||||||
.cb = cb,
|
.cb = cb,
|
||||||
.page = page,
|
.page = page,
|
||||||
|
.arena = arena,
|
||||||
.mode = opts.mode,
|
.mode = opts.mode,
|
||||||
.name = opts.name,
|
.name = opts.name,
|
||||||
.timer_id = timer_id,
|
.timer_id = timer_id,
|
||||||
.params = persisted_params,
|
.params = persisted_params,
|
||||||
.repeat_ms = if (opts.repeat) if (delay_ms == 0) 1 else delay_ms else null,
|
.repeat_ms = if (opts.repeat) if (delay_ms == 0) 1 else delay_ms else null,
|
||||||
});
|
};
|
||||||
gop.value_ptr.* = callback;
|
gop.value_ptr.* = callback;
|
||||||
errdefer page._factory.destroy(callback);
|
|
||||||
|
|
||||||
try page.scheduler.add(callback, ScheduleCallback.run, delay_ms, .{
|
try page.scheduler.add(callback, ScheduleCallback.run, delay_ms, .{
|
||||||
.name = opts.name,
|
.name = opts.name,
|
||||||
.low_priority = opts.low_priority,
|
.low_priority = opts.low_priority,
|
||||||
|
.finalizer = ScheduleCallback.cancelled,
|
||||||
});
|
});
|
||||||
|
|
||||||
return timer_id;
|
return timer_id;
|
||||||
@@ -556,13 +566,11 @@ const ScheduleCallback = struct {
|
|||||||
|
|
||||||
cb: js.Function.Temp,
|
cb: js.Function.Temp,
|
||||||
|
|
||||||
page: *Page,
|
|
||||||
|
|
||||||
params: []const js.Value.Temp,
|
|
||||||
|
|
||||||
removed: bool = false,
|
|
||||||
|
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
page: *Page,
|
||||||
|
arena: Allocator,
|
||||||
|
removed: bool = false,
|
||||||
|
params: []const js.Value.Temp,
|
||||||
|
|
||||||
const Mode = enum {
|
const Mode = enum {
|
||||||
idle,
|
idle,
|
||||||
@@ -570,19 +578,26 @@ const ScheduleCallback = struct {
|
|||||||
animation_frame,
|
animation_frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn cancelled(ctx: *anyopaque) void {
|
||||||
|
var self: *ScheduleCallback = @ptrCast(@alignCast(ctx));
|
||||||
|
self.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
fn deinit(self: *ScheduleCallback) void {
|
fn deinit(self: *ScheduleCallback) void {
|
||||||
self.page.js.release(self.cb);
|
self.page.js.release(self.cb);
|
||||||
for (self.params) |param| {
|
for (self.params) |param| {
|
||||||
self.page.js.release(param);
|
self.page.js.release(param);
|
||||||
}
|
}
|
||||||
self.page._factory.destroy(self);
|
self.page.releaseArena(self.arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(ctx: *anyopaque) !?u32 {
|
fn run(ctx: *anyopaque) !?u32 {
|
||||||
const self: *ScheduleCallback = @ptrCast(@alignCast(ctx));
|
const self: *ScheduleCallback = @ptrCast(@alignCast(ctx));
|
||||||
const page = self.page;
|
const page = self.page;
|
||||||
|
const window = page.window;
|
||||||
|
|
||||||
if (self.removed) {
|
if (self.removed) {
|
||||||
_ = page.window._timers.remove(self.timer_id);
|
_ = window._timers.remove(self.timer_id);
|
||||||
self.deinit();
|
self.deinit();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -599,7 +614,7 @@ const ScheduleCallback = struct {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
.animation_frame => {
|
.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 });
|
log.warn(.js, "window.RAF", .{ .name = self.name, .err = err });
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -614,35 +629,43 @@ const ScheduleCallback = struct {
|
|||||||
return ms;
|
return ms;
|
||||||
}
|
}
|
||||||
defer self.deinit();
|
defer self.deinit();
|
||||||
_ = page.window._timers.remove(self.timer_id);
|
_ = window._timers.remove(self.timer_id);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const PostMessageCallback = struct {
|
const PostMessageCallback = struct {
|
||||||
window: *Window,
|
|
||||||
message: js.Value.Global,
|
|
||||||
origin: []const u8,
|
|
||||||
page: *Page,
|
page: *Page,
|
||||||
|
arena: Allocator,
|
||||||
|
origin: []const u8,
|
||||||
|
message: js.Value.Global,
|
||||||
|
|
||||||
fn deinit(self: *PostMessageCallback) void {
|
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 {
|
fn run(ctx: *anyopaque) !?u32 {
|
||||||
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
const self: *PostMessageCallback = @ptrCast(@alignCast(ctx));
|
||||||
defer self.deinit();
|
defer self.deinit();
|
||||||
|
|
||||||
|
const page = self.page;
|
||||||
|
const window = page.window;
|
||||||
|
|
||||||
const message_event = try MessageEvent.initTrusted("message", .{
|
const message_event = try MessageEvent.initTrusted("message", .{
|
||||||
.data = self.message,
|
.data = self.message,
|
||||||
.origin = self.origin,
|
.origin = self.origin,
|
||||||
.source = self.window,
|
.source = window,
|
||||||
.bubbles = false,
|
.bubbles = false,
|
||||||
.cancelable = false,
|
.cancelable = false,
|
||||||
}, self.page);
|
}, page);
|
||||||
|
|
||||||
const event = message_event.asEvent();
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user