From 8e8a1a7541e33e2bd33905fd2fe3b407538f8683 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 2 Mar 2026 17:51:33 +0100 Subject: [PATCH] use a pool arena with ReadableStream --- src/browser/webapi/streams/ReadableStream.zig | 12 ++++++++++++ .../streams/ReadableStreamDefaultController.zig | 14 +++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/browser/webapi/streams/ReadableStream.zig b/src/browser/webapi/streams/ReadableStream.zig index e4e5d0f9..6dd086fc 100644 --- a/src/browser/webapi/streams/ReadableStream.zig +++ b/src/browser/webapi/streams/ReadableStream.zig @@ -47,6 +47,8 @@ _page: *Page, _state: State, _reader: ?*ReadableStreamDefaultReader, _controller: *ReadableStreamDefaultController, +// The arena is used by the builtin controller. +_arena: std.mem.Allocator, _stored_error: ?[]const u8, _pull_fn: ?js.Function.Global = null, _pulling: bool = false, @@ -68,9 +70,13 @@ const QueueingStrategy = struct { pub fn init(src_: ?UnderlyingSource, strategy_: ?QueueingStrategy, page: *Page) !*ReadableStream { const strategy: QueueingStrategy = strategy_ orelse .{}; + const arena = try page.getArena(.{ .debug = "Animation" }); + errdefer page.releaseArena(arena); + const self = try page._factory.create(ReadableStream{ ._page = page, ._state = .readable, + ._arena = arena, ._reader = null, ._controller = undefined, ._stored_error = null, @@ -108,6 +114,10 @@ pub fn initWithData(data: []const u8, page: *Page) !*ReadableStream { return stream; } +pub fn deinit(self: *ReadableStream, _: bool, page: *Page) void { + page.releaseArena(self._arena); +} + pub fn getReader(self: *ReadableStream, page: *Page) !*ReadableStreamDefaultReader { if (self.getLocked()) { return error.ReaderLocked; @@ -367,6 +377,8 @@ pub const JsApi = struct { pub const name = "ReadableStream"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; + pub const weak = true; + pub const finalizer = bridge.finalizer(ReadableStream.deinit); }; pub const constructor = bridge.constructor(ReadableStream.init, .{}); diff --git a/src/browser/webapi/streams/ReadableStreamDefaultController.zig b/src/browser/webapi/streams/ReadableStreamDefaultController.zig index 18228475..84cb14e7 100644 --- a/src/browser/webapi/streams/ReadableStreamDefaultController.zig +++ b/src/browser/webapi/streams/ReadableStreamDefaultController.zig @@ -27,6 +27,8 @@ const ReadableStreamDefaultReader = @import("ReadableStreamDefaultReader.zig"); const IS_DEBUG = @import("builtin").mode == .Debug; +/// ReadableStreamDefaultController uses ReadableStream's arena to make +/// allocation. Indeed, the controller is owned by its ReadableStream. const ReadableStreamDefaultController = @This(); pub const Chunk = union(enum) { @@ -46,7 +48,6 @@ pub const Chunk = union(enum) { _page: *Page, _stream: *ReadableStream, -_arena: std.mem.Allocator, _queue: std.ArrayList(Chunk), _pending_reads: std.ArrayList(js.PromiseResolver.Global), _high_water_mark: u32, @@ -56,7 +57,6 @@ pub fn init(stream: *ReadableStream, high_water_mark: u32, page: *Page) !*Readab ._page = page, ._queue = .empty, ._stream = stream, - ._arena = page.arena, ._pending_reads = .empty, ._high_water_mark = high_water_mark, }); @@ -64,7 +64,7 @@ pub fn init(stream: *ReadableStream, high_water_mark: u32, page: *Page) !*Readab pub fn addPendingRead(self: *ReadableStreamDefaultController, page: *Page) !js.Promise { const resolver = page.js.local.?.createPromiseResolver(); - try self._pending_reads.append(self._arena, try resolver.persist()); + try self._pending_reads.append(self._stream._arena, try resolver.persist()); return resolver.promise(); } @@ -74,8 +74,8 @@ pub fn enqueue(self: *ReadableStreamDefaultController, chunk: Chunk) !void { } if (self._pending_reads.items.len == 0) { - const chunk_copy = try chunk.dupe(self._page.arena); - return self._queue.append(self._arena, chunk_copy); + const chunk_copy = try chunk.dupe(self._stream._arena); + return self._queue.append(self._stream._arena, chunk_copy); } // I know, this is ouch! But we expect to have very few (if any) @@ -109,7 +109,7 @@ pub fn enqueueValue(self: *ReadableStreamDefaultController, value: js.Value) !vo if (self._pending_reads.items.len == 0) { const persisted = try value.persist(); - try self._queue.append(self._arena, .{ .js_value = persisted }); + try self._queue.append(self._stream._arena, .{ .js_value = persisted }); return; } @@ -170,7 +170,7 @@ pub fn doError(self: *ReadableStreamDefaultController, err: []const u8) !void { } self._stream._state = .errored; - self._stream._stored_error = try self._page.arena.dupe(u8, err); + self._stream._stored_error = try self._stream._arena.dupe(u8, err); // Reject all pending reads for (self._pending_reads.items) |resolver| {