mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Disable the call arena (for now)
The call arena doesn't consider nested calls (like, from callbacks). Currently when a "call" ends, the arena is cleared. But in a callback, if we do that, the memory for the containing code is no longer valid, even though it's still executing. For now, use the existing scope_arena, instead of the call_arena. In the future we need to track the call-depth, and only reset the call_arena when we're done with a top-level statement. Also: -Properly handle callback errors -Increase wpt file size -Merge latest loop.zig from zig-js-runtime.
This commit is contained in:
@@ -873,7 +873,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
|||||||
self.scope = Scope{
|
self.scope = Scope{
|
||||||
.handle_scope = handle_scope,
|
.handle_scope = handle_scope,
|
||||||
.arena = self.scope_arena.allocator(),
|
.arena = self.scope_arena.allocator(),
|
||||||
.call_arena = self.call_arena.allocator(),
|
.call_arena = self.scope_arena.allocator(),
|
||||||
};
|
};
|
||||||
_ = try self._mapZigInstanceToJs(self.context.getGlobal(), global);
|
_ = try self._mapZigInstanceToJs(self.context.getGlobal(), global);
|
||||||
}
|
}
|
||||||
@@ -1128,7 +1128,19 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
|||||||
inline for (fields, 0..) |f, i| {
|
inline for (fields, 0..) |f, i| {
|
||||||
js_args[i] = try executor.zigValueToJs(@field(aargs, f.name));
|
js_args[i] = try executor.zigValueToJs(@field(aargs, f.name));
|
||||||
}
|
}
|
||||||
_ = self.func.castToFunction().call(executor.context, js_this, &js_args);
|
|
||||||
|
const result = self.func.castToFunction().call(executor.context, js_this, &js_args);
|
||||||
|
if (result == null) {
|
||||||
|
return error.JSExecCallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug/helper to print the source of the JS callback
|
||||||
|
fn printFunc(self: *const @This()) !void {
|
||||||
|
const executor = self.executor;
|
||||||
|
const value = self.func.castToFunction().toValue();
|
||||||
|
const src = try valueToString(executor.call_arena.allocator(), value, executor.isolate, executor.context);
|
||||||
|
std.debug.print("{s}\n", .{src});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1405,7 +1417,8 @@ fn Caller(comptime E: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
fn deinit(self: *Self) void {
|
||||||
_ = self.executor.call_arena.reset(.{ .retain_with_limit = 4096 });
|
_ = self;
|
||||||
|
// _ = self.executor.call_arena.reset(.{ .retain_with_limit = 4096 });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constructor(self: *Self, comptime named_function: anytype, info: v8.FunctionCallbackInfo) !void {
|
fn constructor(self: *Self, comptime named_function: anytype, info: v8.FunctionCallbackInfo) !void {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
const MemoryPool = std.heap.MemoryPool;
|
const MemoryPool = std.heap.MemoryPool;
|
||||||
|
|
||||||
pub const IO = @import("tigerbeetle-io").IO;
|
pub const IO = @import("tigerbeetle-io").IO;
|
||||||
@@ -35,8 +36,12 @@ const log = std.log.scoped(.loop);
|
|||||||
pub const Loop = struct {
|
pub const Loop = struct {
|
||||||
alloc: std.mem.Allocator, // TODO: unmanaged version ?
|
alloc: std.mem.Allocator, // TODO: unmanaged version ?
|
||||||
io: IO,
|
io: IO,
|
||||||
|
|
||||||
|
// both events_nb are used to track how many callbacks are to be called.
|
||||||
|
// We use these counters to wait until all the events are finished.
|
||||||
js_events_nb: usize,
|
js_events_nb: usize,
|
||||||
zig_events_nb: usize,
|
zig_events_nb: usize,
|
||||||
|
|
||||||
cbk_error: bool = false,
|
cbk_error: bool = false,
|
||||||
|
|
||||||
// js_ctx_id is incremented each time the loop is reset for JS.
|
// js_ctx_id is incremented each time the loop is reset for JS.
|
||||||
@@ -51,6 +56,11 @@ pub const Loop = struct {
|
|||||||
// This is a weak way to cancel all future Zig callbacks.
|
// This is a weak way to cancel all future Zig callbacks.
|
||||||
zig_ctx_id: u32 = 0,
|
zig_ctx_id: u32 = 0,
|
||||||
|
|
||||||
|
// The MacOS event loop doesn't support cancellation. We use this to track
|
||||||
|
// cancellation ids and, on the timeout callback, we can can check here
|
||||||
|
// to see if it's been cancelled.
|
||||||
|
cancelled: std.AutoHashMapUnmanaged(usize, void),
|
||||||
|
|
||||||
cancel_pool: MemoryPool(ContextCancel),
|
cancel_pool: MemoryPool(ContextCancel),
|
||||||
timeout_pool: MemoryPool(ContextTimeout),
|
timeout_pool: MemoryPool(ContextTimeout),
|
||||||
event_callback_pool: MemoryPool(EventCallbackContext),
|
event_callback_pool: MemoryPool(EventCallbackContext),
|
||||||
@@ -65,6 +75,7 @@ pub const Loop = struct {
|
|||||||
pub fn init(alloc: std.mem.Allocator) !Self {
|
pub fn init(alloc: std.mem.Allocator) !Self {
|
||||||
return Self{
|
return Self{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
|
.cancelled = .{},
|
||||||
.io = try IO.init(32, 0),
|
.io = try IO.init(32, 0),
|
||||||
.js_events_nb = 0,
|
.js_events_nb = 0,
|
||||||
.zig_events_nb = 0,
|
.zig_events_nb = 0,
|
||||||
@@ -75,6 +86,12 @@ pub const Loop = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
|
// first disable callbacks for existing events.
|
||||||
|
// We don't want a callback re-create a setTimeout, it could create an
|
||||||
|
// infinite loop on wait for events.
|
||||||
|
self.resetJS();
|
||||||
|
self.resetZig();
|
||||||
|
|
||||||
// run tail events. We do run the tail events to ensure all the
|
// run tail events. We do run the tail events to ensure all the
|
||||||
// contexts are correcly free.
|
// contexts are correcly free.
|
||||||
while (self.eventsNb(.js) > 0 or self.eventsNb(.zig) > 0) {
|
while (self.eventsNb(.js) > 0 or self.eventsNb(.zig) > 0) {
|
||||||
@@ -83,11 +100,14 @@ pub const Loop = struct {
|
|||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.cancelAll();
|
if (comptime CANCEL_SUPPORTED) {
|
||||||
|
self.io.cancel_all();
|
||||||
|
}
|
||||||
self.io.deinit();
|
self.io.deinit();
|
||||||
self.cancel_pool.deinit();
|
self.cancel_pool.deinit();
|
||||||
self.timeout_pool.deinit();
|
self.timeout_pool.deinit();
|
||||||
self.event_callback_pool.deinit();
|
self.event_callback_pool.deinit();
|
||||||
|
self.cancelled.deinit(self.alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve all registred I/O events completed by OS kernel,
|
// Retrieve all registred I/O events completed by OS kernel,
|
||||||
@@ -131,9 +151,6 @@ pub const Loop = struct {
|
|||||||
fn eventsNb(self: *Self, comptime event: Event) usize {
|
fn eventsNb(self: *Self, comptime event: Event) usize {
|
||||||
return @atomicLoad(usize, self.eventsPtr(event), .seq_cst);
|
return @atomicLoad(usize, self.eventsPtr(event), .seq_cst);
|
||||||
}
|
}
|
||||||
fn resetEvents(self: *Self, comptime event: Event) void {
|
|
||||||
@atomicStore(usize, self.eventsPtr(event), 0, .unordered);
|
|
||||||
}
|
|
||||||
|
|
||||||
// JS callbacks APIs
|
// JS callbacks APIs
|
||||||
// -----------------
|
// -----------------
|
||||||
@@ -158,6 +175,12 @@ pub const Loop = struct {
|
|||||||
loop.alloc.destroy(completion);
|
loop.alloc.destroy(completion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (comptime CANCEL_SUPPORTED == false) {
|
||||||
|
if (loop.cancelled.remove(@intFromPtr(completion))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the loop's context id has changed, don't call the js callback
|
// If the loop's context id has changed, don't call the js callback
|
||||||
// function. The callback's memory has already be cleaned and the
|
// function. The callback's memory has already be cleaned and the
|
||||||
// events nb reset.
|
// events nb reset.
|
||||||
@@ -175,7 +198,7 @@ pub const Loop = struct {
|
|||||||
// js callback
|
// js callback
|
||||||
if (ctx.js_cbk) |*js_cbk| {
|
if (ctx.js_cbk) |*js_cbk| {
|
||||||
js_cbk.call(null) catch {
|
js_cbk.call(null) catch {
|
||||||
ctx.loop.cbk_error = true;
|
loop.cbk_error = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,19 +257,26 @@ pub const Loop = struct {
|
|||||||
// js callback
|
// js callback
|
||||||
if (ctx.js_cbk) |*js_cbk| {
|
if (ctx.js_cbk) |*js_cbk| {
|
||||||
js_cbk.call(null) catch {
|
js_cbk.call(null) catch {
|
||||||
ctx.loop.cbk_error = true;
|
loop.cbk_error = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel(self: *Self, id: usize, js_cbk: ?JSCallback) !void {
|
pub fn cancel(self: *Self, id: usize, js_cbk: ?JSCallback) !void {
|
||||||
if (IO.supports_cancel == false) {
|
const alloc = self.alloc;
|
||||||
|
if (comptime CANCEL_SUPPORTED == false) {
|
||||||
|
try self.cancelled.put(alloc, id, {});
|
||||||
|
if (js_cbk) |cbk| {
|
||||||
|
cbk.call(null) catch {
|
||||||
|
self.cbk_error = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const comp_cancel: *IO.Completion = @ptrFromInt(id);
|
const comp_cancel: *IO.Completion = @ptrFromInt(id);
|
||||||
|
|
||||||
const completion = try self.alloc.create(Completion);
|
const completion = try alloc.create(Completion);
|
||||||
errdefer self.alloc.destroy(completion);
|
errdefer alloc.destroy(completion);
|
||||||
completion.* = undefined;
|
completion.* = undefined;
|
||||||
|
|
||||||
const ctx = self.alloc.create(ContextCancel) catch unreachable;
|
const ctx = self.alloc.create(ContextCancel) catch unreachable;
|
||||||
@@ -260,18 +290,17 @@ pub const Loop = struct {
|
|||||||
self.io.cancel_one(*ContextCancel, ctx, cancelCallback, completion, comp_cancel);
|
self.io.cancel_one(*ContextCancel, ctx, cancelCallback, completion, comp_cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cancelAll(self: *Self) void {
|
|
||||||
self.resetEvents(.js);
|
|
||||||
self.resetEvents(.zig);
|
|
||||||
self.io.cancel_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset all existing JS callbacks.
|
// Reset all existing JS callbacks.
|
||||||
|
// The existing events will happen and their memory will be cleanup but the
|
||||||
|
// corresponding callbacks will not be called.
|
||||||
pub fn resetJS(self: *Self) void {
|
pub fn resetJS(self: *Self) void {
|
||||||
self.js_ctx_id += 1;
|
self.js_ctx_id += 1;
|
||||||
|
self.cancelled.clearRetainingCapacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all existing Zig callbacks.
|
// Reset all existing Zig callbacks.
|
||||||
|
// The existing events will happen and their memory will be cleanup but the
|
||||||
|
// corresponding callbacks will not be called.
|
||||||
pub fn resetZig(self: *Self) void {
|
pub fn resetZig(self: *Self) void {
|
||||||
self.zig_ctx_id += 1;
|
self.zig_ctx_id += 1;
|
||||||
}
|
}
|
||||||
@@ -365,6 +394,7 @@ pub const Loop = struct {
|
|||||||
const ContextZigTimeout = struct {
|
const ContextZigTimeout = struct {
|
||||||
loop: *Self,
|
loop: *Self,
|
||||||
zig_ctx_id: u32,
|
zig_ctx_id: u32,
|
||||||
|
|
||||||
context: *anyopaque,
|
context: *anyopaque,
|
||||||
callback: *const fn (
|
callback: *const fn (
|
||||||
context: ?*anyopaque,
|
context: ?*anyopaque,
|
||||||
@@ -431,3 +461,9 @@ const EventCallbackContext = struct {
|
|||||||
ctx: *anyopaque,
|
ctx: *anyopaque,
|
||||||
loop: *Loop,
|
loop: *Loop,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CANCEL_SUPPORTED = switch (builtin.target.os.tag) {
|
||||||
|
.linux => true,
|
||||||
|
.macos, .tvos, .watchos, .ios => false,
|
||||||
|
else => @compileError("IO is not supported for platform"),
|
||||||
|
};
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub fn run(arena: Allocator, comptime dir: []const u8, f: []const u8, loader: *F
|
|||||||
const html = blk: {
|
const html = blk: {
|
||||||
const file = try std.fs.cwd().openFile(f, .{});
|
const file = try std.fs.cwd().openFile(f, .{});
|
||||||
defer file.close();
|
defer file.close();
|
||||||
break :blk try file.readToEndAlloc(arena, 16 * 1024);
|
break :blk try file.readToEndAlloc(arena, 128 * 1024);
|
||||||
};
|
};
|
||||||
|
|
||||||
const dirname = fspath.dirname(f[dir.len..]) orelse unreachable;
|
const dirname = fspath.dirname(f[dir.len..]) orelse unreachable;
|
||||||
|
|||||||
Reference in New Issue
Block a user