Expand the lifetime of the XHR reference

We need to take the self-reference to the XHR object as soon as the request is
made. Previously, we were waiting until we got the start callback, but v8 could
(and does) drop the reference before that happens. Unfortunately, that means
we can no longer use _transfer == null to tell if we own a reference or not, so
a new boolean was added.
This commit is contained in:
Karl Seguin
2026-03-31 16:46:31 +08:00
parent 55666ab7cd
commit 492fd86bad
2 changed files with 17 additions and 3 deletions

View File

@@ -44,6 +44,7 @@ _page: *Page,
_proto: *XMLHttpRequestEventTarget, _proto: *XMLHttpRequestEventTarget,
_arena: Allocator, _arena: Allocator,
_transfer: ?*HttpClient.Transfer = null, _transfer: ?*HttpClient.Transfer = null,
_has_ref: bool = false,
_url: [:0]const u8 = "", _url: [:0]const u8 = "",
_method: net_http.Method = .GET, _method: net_http.Method = .GET,
@@ -136,6 +137,14 @@ pub fn deinit(self: *XMLHttpRequest, session: *Session) void {
session.releaseArena(self._arena); session.releaseArena(self._arena);
} }
fn releaseSelfRef(self: *XMLHttpRequest) void {
if (self._has_ref == false) {
return;
}
self.releaseRef(self._page._session);
self._has_ref = false;
}
pub fn releaseRef(self: *XMLHttpRequest, session: *Session) void { pub fn releaseRef(self: *XMLHttpRequest, session: *Session) void {
self._rc.release(self, session); self._rc.release(self, session);
} }
@@ -252,6 +261,8 @@ pub fn send(self: *XMLHttpRequest, body_: ?[]const u8) !void {
.error_callback = httpErrorCallback, .error_callback = httpErrorCallback,
.shutdown_callback = httpShutdownCallback, .shutdown_callback = httpShutdownCallback,
}); });
self.acquireRef();
self._has_ref = true;
} }
fn handleBlobUrl(self: *XMLHttpRequest, page: *Page) !void { fn handleBlobUrl(self: *XMLHttpRequest, page: *Page) !void {
@@ -393,7 +404,6 @@ fn httpStartCallback(transfer: *HttpClient.Transfer) !void {
log.debug(.http, "request start", .{ .method = self._method, .url = self._url, .source = "xhr" }); log.debug(.http, "request start", .{ .method = self._method, .url = self._url, .source = "xhr" });
} }
self._transfer = transfer; self._transfer = transfer;
self.acquireRef();
} }
fn httpHeaderCallback(transfer: *HttpClient.Transfer, header: net_http.Header) !void { fn httpHeaderCallback(transfer: *HttpClient.Transfer, header: net_http.Header) !void {
@@ -501,8 +511,8 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
self.handleError(err); self.handleError(err);
if (self._transfer != null) { if (self._transfer != null) {
self._transfer = null; self._transfer = null;
self.releaseRef(self._page._session);
} }
self.releaseSelfRef();
} }
fn httpShutdownCallback(ctx: *anyopaque) void { fn httpShutdownCallback(ctx: *anyopaque) void {
@@ -515,8 +525,8 @@ pub fn abort(self: *XMLHttpRequest) void {
if (self._transfer) |transfer| { if (self._transfer) |transfer| {
self._transfer = null; self._transfer = null;
transfer.abort(error.Abort); transfer.abort(error.Abort);
self.releaseRef(self._page._session);
} }
self.releaseSelfRef();
} }
fn handleError(self: *XMLHttpRequest, err: anyerror) void { fn handleError(self: *XMLHttpRequest, err: anyerror) void {

View File

@@ -237,6 +237,10 @@ pub fn RC(comptime T: type) type {
session.releaseArena(kv.value.arena); session.releaseArena(kv.value.arena);
} }
} }
pub fn format(self: @This(), writer: *std.Io.Writer) !void {
return writer.print("{d}", .{self._refs});
}
}; };
} }