diff --git a/src/ArenaPool.zig b/src/ArenaPool.zig index 32257e30..7f1d38dc 100644 --- a/src/ArenaPool.zig +++ b/src/ArenaPool.zig @@ -29,6 +29,7 @@ free_list_len: u16 = 0, free_list: ?*Entry = null, free_list_max: u16, entry_pool: std.heap.MemoryPool(Entry), +mutex: std.Thread.Mutex = .{}, const Entry = struct { next: ?*Entry, @@ -54,6 +55,9 @@ pub fn deinit(self: *ArenaPool) void { } pub fn acquire(self: *ArenaPool) !Allocator { + self.mutex.lock(); + defer self.mutex.unlock(); + if (self.free_list) |entry| { self.free_list = entry.next; self.free_list_len -= 1; @@ -73,6 +77,12 @@ pub fn release(self: *ArenaPool, allocator: Allocator) void { const arena: *std.heap.ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); const entry: *Entry = @fieldParentPtr("arena", arena); + // Reset the arena before acquiring the lock to minimize lock hold time + _ = arena.reset(.{ .retain_with_limit = self.retain_bytes }); + + self.mutex.lock(); + defer self.mutex.unlock(); + const free_list_len = self.free_list_len; if (free_list_len == self.free_list_max) { arena.deinit(); @@ -80,7 +90,6 @@ pub fn release(self: *ArenaPool, allocator: Allocator) void { return; } - _ = arena.reset(.{ .retain_with_limit = self.retain_bytes }); entry.next = self.free_list; self.free_list_len = free_list_len + 1; self.free_list = entry; diff --git a/src/browser/Robots.zig b/src/browser/Robots.zig index 5c0428fa..0318ef30 100644 --- a/src/browser/Robots.zig +++ b/src/browser/Robots.zig @@ -111,12 +111,16 @@ pub const RobotStore = struct { allocator: std.mem.Allocator, map: RobotsMap, + mutex: std.Thread.Mutex = .{}, pub fn init(allocator: std.mem.Allocator) RobotStore { return .{ .allocator = allocator, .map = .empty }; } pub fn deinit(self: *RobotStore) void { + self.mutex.lock(); + defer self.mutex.unlock(); + var iter = self.map.iterator(); while (iter.next()) |entry| { @@ -132,6 +136,9 @@ pub const RobotStore = struct { } pub fn get(self: *RobotStore, url: []const u8) ?RobotsEntry { + self.mutex.lock(); + defer self.mutex.unlock(); + return self.map.get(url); } @@ -140,11 +147,17 @@ pub const RobotStore = struct { } pub fn put(self: *RobotStore, url: []const u8, robots: Robots) !void { + self.mutex.lock(); + defer self.mutex.unlock(); + const duped = try self.allocator.dupe(u8, url); try self.map.put(self.allocator, duped, .{ .present = robots }); } pub fn putAbsent(self: *RobotStore, url: []const u8) !void { + self.mutex.lock(); + defer self.mutex.unlock(); + const duped = try self.allocator.dupe(u8, url); try self.map.put(self.allocator, duped, .absent); } diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 8b64ad1f..ef0cc1f6 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -39,6 +39,14 @@ const JsApis = bridge.JsApis; const Allocator = std.mem.Allocator; const IS_DEBUG = builtin.mode == .Debug; +fn initClassIds() void { + inline for (JsApis, 0..) |JsApi, i| { + JsApi.Meta.class_id = i; + } +} + +var class_id_once = std.once(initClassIds); + // The Env maps to a V8 isolate, which represents a isolated sandbox for // executing JavaScript. The Env is where we'll define our V8 <-> Zig bindings, // and it's where we'll start ExecutionWorlds, which actually execute JavaScript. @@ -90,6 +98,9 @@ pub fn init(app: *App, opts: InitOpts) !Env { } } + // Initialize class IDs once before any V8 work + class_id_once.call(); + const allocator = app.allocator; const snapshot = &app.snapshot; @@ -132,8 +143,7 @@ pub fn init(app: *App, opts: InitOpts) !Env { temp_scope.init(isolate); defer temp_scope.deinit(); - inline for (JsApis, 0..) |JsApi, i| { - JsApi.Meta.class_id = i; + inline for (JsApis, 0..) |_, i| { const data = v8.v8__Isolate__GetDataFromSnapshotOnce(isolate_handle, snapshot.data_start + i); const function_handle: *const v8.FunctionTemplate = @ptrCast(data); // Make function template eternal