From 1adc8a3b706146cf8024f7986e39d300f34b427f Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Fri, 21 Aug 2026 09:30:28 +0800 Subject: [PATCH] mem: Cleanup arena leak on page shutdown with live animations --- src/browser/webapi/animation/Animation.zig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/browser/webapi/animation/Animation.zig b/src/browser/webapi/animation/Animation.zig index e21289458..21a7275c2 100644 --- a/src/browser/webapi/animation/Animation.zig +++ b/src/browser/webapi/animation/Animation.zig @@ -84,14 +84,22 @@ pub fn play(self: *Animation, frame: *Frame) !void { // Schedule the transition from .running => .finished in 10ms. self.acquireRef(); + errdefer self.releaseRef(frame._page); try frame.js.scheduler.add( self, Animation.update, 10, - .{ .name = "animation.update" }, + .{ .name = "animation.update", .finalizer = Animation.cancelled }, ); } +// The scheduler drops pending tasks when the context is torn down. `update` +// and `cancelled` are mutually exclusive, so play()'s ref is released once. +fn cancelled(ctx: *anyopaque) void { + const self: *Animation = @ptrCast(@alignCast(ctx)); + self.releaseRef(self._frame._page); +} + pub fn pause(self: *Animation) void { self._playState = .paused; }