From 4f81cb93333531d9f77cbc412f3899ae5b2478ab Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sat, 14 Feb 2026 20:01:09 +0800 Subject: [PATCH] Add more granular assertions Trying to see how the "ScriptManager.Header buffer" assertion is failing. Either `headerCallback` is being called multiple times, or the script is corrupt. By adding a similar assertion in various places, we can hopefully narrow (a) what's going on and (b) what code is involved. Also, switched the BufferPool from DoublyLinkedList to SinglyLinkedList. Was just reviewing this code (to see if the buffer could possibly become corrupt) and realized this could be switched. --- src/browser/ScriptManager.zig | 15 ++++++++++++--- src/http/Client.zig | 8 ++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index eb6e5e6d..4bfec58a 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -617,6 +617,7 @@ pub const Script = struct { node: std.DoublyLinkedList.Node, script_element: ?*Element.Html.Script, manager: *ScriptManager, + header_callback_called: bool = false, const Kind = enum { module, @@ -681,7 +682,15 @@ pub const Script = struct { }); } - lp.assert(self.source.remote.capacity == 0, "ScriptManager.HeaderCallback", .{ .capacity = self.source.remote.capacity }); + { + // temp debug, trying to figure out why the next assert sometimes + // fails. Is the buffer just corrupt or is headerCallback really + // being called twice? + lp.assert(self.header_callback_called == false, "ScriptManager.Header recall", .{}); + self.header_callback_called = true; + } + + lp.assert(self.source.remote.capacity == 0, "ScriptManager.Header buffer", .{ .capacity = self.source.remote.capacity }); var buffer = self.manager.buffer_pool.get(); if (transfer.getContentLength()) |cl| { try buffer.ensureTotalCapacity(self.manager.allocator, cl); @@ -894,7 +903,7 @@ const BufferPool = struct { max_concurrent_transfers: u8, mem_pool: std.heap.MemoryPool(Container), - const List = std.DoublyLinkedList; + const List = std.SinglyLinkedList; const Container = struct { node: List.Node, @@ -953,7 +962,7 @@ const BufferPool = struct { b.clearRetainingCapacity(); container.* = .{ .buf = b, .node = .{} }; self.count += 1; - self.available.append(&container.node); + self.available.prepend(&container.node); } }; diff --git a/src/http/Client.zig b/src/http/Client.zig index 6e317d10..ea86e590 100644 --- a/src/http/Client.zig +++ b/src/http/Client.zig @@ -557,6 +557,7 @@ pub fn fulfillTransfer(self: *Client, transfer: *Transfer, status: u16, headers: try transfer.fulfill(status, headers, body); if (!transfer.req.blocking) { transfer.deinit(); + return; } transfer._intercept_state = .fulfilled; } @@ -1143,7 +1144,7 @@ pub const Transfer = struct { // against that, so resetting it would allow a 2nd call to headerCallback). // But it should also be impossible for this to be true. So, I've added // this assertion to try to narrow down what's going on. - lp.assert(self._header_done_called == false, "Transert.reset header_done_called", .{}); + lp.assert(self._header_done_called == false, "Transfer.reset header_done_called", .{}); self._redirecting = false; self._auth_challenge = null; @@ -1340,9 +1341,7 @@ pub const Transfer = struct { // It can be called either on dataCallback or once the request for those // w/o body. fn headerDoneCallback(transfer: *Transfer, easy: *c.CURL) !bool { - if (comptime IS_DEBUG) { - std.debug.assert(transfer._header_done_called == false); - } + lp.assert(transfer._header_done_called == false, "Transfer.headerDoneCallback", .{}); defer transfer._header_done_called = true; try transfer.buildResponseHeader(easy); @@ -1608,6 +1607,7 @@ pub const Transfer = struct { } } + lp.assert(transfer._header_done_called == false, "Transfer.fulfill header_done_called", .{}); if (try req.header_callback(transfer) == false) { transfer.abort(error.Abort); return;