From 625e240f5ac02ca61d18ee575091b0ea960b27c5 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 13 May 2026 17:58:49 +0800 Subject: [PATCH] Pump the http_client queue after perform, not just before MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Client.tick drains self.queue (assigning conns to queued transfers) only at the start. When perform / processMessages releases a batch of conns back to the pool, those conns sit idle until the next tick — a queued transfer that could have run this tick waits one Runner iteration (~20 ms in the test runner) for no reason. Adds a second drainQueue call after perform so newly-freed conns get picked up immediately. In practice this matters whenever httpMaxHostOpen / httpMaxConcurrent is exceeded — pages with N > limit subresources had each "wave" of queue overflow paying one extra tick of latency. --- src/browser/HttpClient.zig | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/browser/HttpClient.zig b/src/browser/HttpClient.zig index 7c7bdd5e..26bcf453 100644 --- a/src/browser/HttpClient.zig +++ b/src/browser/HttpClient.zig @@ -379,19 +379,27 @@ pub fn abortRequests(_: *Client, owner: *Owner) void { } pub fn tick(self: *Client, timeout_ms: u32) !PerformStatus { + try self.drainQueue(); + const status = try self.perform(@intCast(timeout_ms)); + // perform/processMessages just released a batch of connections back to + // the pool. Drain again so queued transfers can use them this tick + // instead of waiting for the next runner iteration. + try self.drainQueue(); + return status; +} + +fn drainQueue(self: *Client) !void { while (self.queue.popFirst()) |queue_node| { const transfer: *Transfer = @fieldParentPtr("_node", queue_node); const conn = self.network.getConnection() orelse { self.queue.prepend(queue_node); - break; + return; }; // Cleared only after we've successfully obtained a connection; // if we put the node back, _queued stays true. transfer._queued = false; try self.makeRequest(conn, transfer); } - - return self.perform(@intCast(timeout_ms)); } // last layer