From a5dfe8ab28f027e0b2a5860df26ec7ea6536ad0d Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Fri, 17 Oct 2025 17:27:33 +0200 Subject: [PATCH 1/2] handle multiple waiters for the same module --- src/browser/ScriptManager.zig | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index b166ca74..2dc43fe2 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -326,15 +326,28 @@ pub fn waitForModule(self: *ScriptManager, url: [:0]const u8) !GetResult { }; const sync = entry.value_ptr.*; + // We can have multiple scripts waiting for the same module in concurrency. + // We use the waiters to ensures only the last waiter deinit the resources. + sync.waiters += 1; + defer sync.waiters -= 1; + var client = self.client; while (true) { switch (sync.state) { .loading => {}, .done => { - // Our caller has its own higher level cache (caching the - // actual compiled module). There's no reason for us to keep this - defer self.sync_module_pool.destroy(sync); - defer self.sync_modules.removeByPtr(entry.key_ptr); + if (sync.waiters == 1) { + // Our caller has its own higher level cache (caching the + // actual compiled module). There's no reason for us to keep + // this if we are the last waiter. + defer self.sync_module_pool.destroy(sync); + defer self.sync_modules.removeByPtr(entry.key_ptr); + return .{ + .buffer = sync.buffer, + .buffer_pool = &self.buffer_pool, + }; + } + return .{ .buffer = sync.buffer, .buffer_pool = &self.buffer_pool, @@ -882,6 +895,8 @@ const SyncModule = struct { manager: *ScriptManager, buffer: std.ArrayListUnmanaged(u8) = .{}, state: State = .loading, + // number of waiters for the module. + waiters: u8 = 0, const State = union(enum) { done, From 9f4e3bf7922d4e6c8831e1ad542c64fd05442e18 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Fri, 17 Oct 2025 18:02:21 +0200 Subject: [PATCH 2/2] add a shared boolean to GetResult to avoid deinit --- src/browser/ScriptManager.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index 2dc43fe2..da46628d 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -343,12 +343,14 @@ pub fn waitForModule(self: *ScriptManager, url: [:0]const u8) !GetResult { defer self.sync_module_pool.destroy(sync); defer self.sync_modules.removeByPtr(entry.key_ptr); return .{ + .shared = false, .buffer = sync.buffer, .buffer_pool = &self.buffer_pool, }; } return .{ + .shared = true, .buffer = sync.buffer, .buffer_pool = &self.buffer_pool, }; @@ -1012,6 +1014,7 @@ pub const AsyncModule = struct { var self: *AsyncModule = @ptrCast(@alignCast(ctx)); defer self.manager.async_module_pool.destroy(self); self.cb(self.cb_data, .{ + .shared = false, .buffer = self.buffer, .buffer_pool = &self.manager.buffer_pool, }); @@ -1035,8 +1038,13 @@ pub const AsyncModule = struct { pub const GetResult = struct { buffer: std.ArrayListUnmanaged(u8), buffer_pool: *BufferPool, + shared: bool, pub fn deinit(self: *GetResult) void { + // if the result is shared, don't deinit. + if (self.shared) { + return; + } self.buffer_pool.release(self.buffer); }