From 093555b3741fee308001602f479a88af5b79d142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sat, 25 Apr 2026 08:19:49 +0200 Subject: [PATCH] browser: hint v8 gc during long waits Runner._wait can iterate for the full opts.ms budget (up to 30s in fetch, longer in agent tool-use loops). V8 was only nudged to GC on session/page teardown (Browser.deinit, Page.deinit), so a page that stays alive while running heavy JS accumulates wrappers and external-ref'd Zig allocations V8 has no reason to drop. Fire memoryPressureNotification(.moderate) once per second from the wait loop. --- src/browser/Runner.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/browser/Runner.zig b/src/browser/Runner.zig index b6274eb0..68a7ad59 100644 --- a/src/browser/Runner.zig +++ b/src/browser/Runner.zig @@ -72,7 +72,21 @@ fn _wait(self: *Runner, comptime is_cdp: bool, opts: WaitOpts) !CDPWaitResult { .ms = 200, .until = opts.until, }; + + // Periodic V8 GC hint during long waits. V8 is otherwise only nudged on + // session/page teardown (Browser.zig, Page.zig), so a page that stays + // alive for seconds while running heavy JS accumulates wrappers and + // external-ref'd Zig allocations V8 has no reason to drop. `.moderate` + // speeds up incremental GC without stalling the tick. + const gc_hint_period_ns: u64 = std.time.ns_per_s; + var gc_hint_timer = std.time.Timer.start() catch unreachable; + while (true) { + if (gc_hint_timer.read() >= gc_hint_period_ns) { + gc_hint_timer.reset(); + self.session.browser.env.memoryPressureNotification(.moderate); + } + const tick_result = self._tick(is_cdp, tick_opts) catch |err| { switch (err) { error.JsError => {}, // already logged (with hopefully more context)