Skip to content

Commit 52634dd

Browse files
committed
Allow webapis to register a destructor to do cleanup on scope (page) end
Add destructor to XHR to abort any inflight requests.
1 parent 812f4d2 commit 52634dd

File tree

3 files changed

+57
-12
lines changed

3 files changed

+57
-12
lines changed

src/browser/xhr/xhr.zig

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ pub const XMLHttpRequest = struct {
257257
};
258258
}
259259

260+
pub fn destructor(self: *XMLHttpRequest, _: anytype) void {
261+
const request = &(self.request orelse return);
262+
request.abort();
263+
}
264+
260265
pub fn reset(self: *XMLHttpRequest) void {
261266
self.url = null;
262267

@@ -278,13 +283,6 @@ pub const XMLHttpRequest = struct {
278283
self.priv_state = .new;
279284
}
280285

281-
pub fn deinit(self: *XMLHttpRequest, alloc: Allocator) void {
282-
if (self.response_obj) |v| {
283-
v.deinit();
284-
}
285-
self.proto.deinit(alloc);
286-
}
287-
288286
pub fn get_readyState(self: *XMLHttpRequest) u16 {
289287
return @intFromEnum(self.state);
290288
}
@@ -518,6 +516,7 @@ pub const XMLHttpRequest = struct {
518516
return;
519517
}
520518

519+
self.request = null;
521520
self.state = .done;
522521
self.send_flag = false;
523522
self.dispatchEvt("readystatechange");

src/http/client.zig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,13 @@ pub const Request = struct {
261261
self._client.state_pool.release(self._state);
262262
}
263263

264+
pub fn abort(self: *Request) void {
265+
const connection = self._connection orelse return;
266+
self.destroyConnection(connection);
267+
self._connection = null;
268+
self.deinit();
269+
}
270+
264271
const DecomposedURL = struct {
265272
secure: bool,
266273
connect_port: u16,

src/runtime/js.zig

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -502,31 +502,34 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
502502

503503
// Callbacks are PesistendObjects. When the scope ends, we need
504504
// to free every callback we created.
505-
callbacks: std.ArrayListUnmanaged(v8.Persistent(v8.Function)) = .{},
505+
callbacks: std.ArrayListUnmanaged(v8.Persistent(v8.Function)) = .empty,
506506

507507
// Serves two purposes. Like `callbacks` above, this is used to free
508508
// every PeristentObjet we've created during the lifetime of the scope.
509509
// More importantly, it serves as an identity map - for a given Zig
510510
// instance, we map it to the same PersistentObject.
511511
// The key is the @intFromPtr of the Zig value
512-
identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .{},
512+
identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .empty,
513513

514514
// Similar to the identity map, but used much less frequently. Some
515515
// web APIs have to manage opaque values. Ideally, they use an
516516
// JsObject, but the JsObject has no lifetime guarantee beyond the
517517
// current call. They can call .persist() on their JsObject to get
518518
// a `*PersistentObject()`. We need to track these to free them.
519519
// The key is the @intFromPtr of the v8.Object.handle.
520-
js_object_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .{},
520+
js_object_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .empty,
521521

522522
// When we need to load a resource (i.e. an external script), we call
523523
// this function to get the source. This is always a reference to the
524524
// Page's fetchModuleSource, but we use a function pointer
525525
// since this js module is decoupled from the browser implementation.
526526
module_loader: ModuleLoader,
527527

528+
// Some Zig types have code to execute to cleanup
529+
destructor_callbacks: std.ArrayListUnmanaged(DestructorCallback) = .empty,
530+
528531
// Some Zig types have code to execute when the call scope ends
529-
call_scope_end_callbacks: std.ArrayListUnmanaged(CallScopeEndCallback) = .{},
532+
call_scope_end_callbacks: std.ArrayListUnmanaged(CallScopeEndCallback) = .empty,
530533

531534
const ModuleLoader = struct {
532535
ptr: *anyopaque,
@@ -536,6 +539,10 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
536539
// no init, started with executor.startScope()
537540

538541
fn deinit(self: *Scope) void {
542+
for (self.destructor_callbacks.items) |cb| {
543+
cb.destructor(self);
544+
}
545+
539546
{
540547
var it = self.identity_map.valueIterator();
541548
while (it.next()) |p| {
@@ -694,6 +701,10 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
694701
return gop.value_ptr.*;
695702
}
696703

704+
if (comptime @hasDecl(ptr.child, "destructor")) {
705+
try self.destructor_callbacks.append(scope_arena, DestructorCallback.init(value));
706+
}
707+
697708
if (comptime @hasDecl(ptr.child, "jsCallScopeEnd")) {
698709
try self.call_scope_end_callbacks.append(scope_arena, CallScopeEndCallback.init(value));
699710
}
@@ -1672,7 +1683,35 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
16721683
}
16731684
}
16741685

1675-
// An interface for types that want to their jsScopeEnd function to be
1686+
// An interface for types that want to have their jsDeinit function to be
1687+
// called when the call scope ends
1688+
const DestructorCallback = struct {
1689+
ptr: *anyopaque,
1690+
destructorFn: *const fn (ptr: *anyopaque, scope: *Scope) void,
1691+
1692+
fn init(ptr: anytype) DestructorCallback {
1693+
const T = @TypeOf(ptr);
1694+
const ptr_info = @typeInfo(T);
1695+
1696+
const gen = struct {
1697+
pub fn destructor(pointer: *anyopaque, scope: *Scope) void {
1698+
const self: T = @ptrCast(@alignCast(pointer));
1699+
return ptr_info.pointer.child.destructor(self, scope);
1700+
}
1701+
};
1702+
1703+
return .{
1704+
.ptr = ptr,
1705+
.destructorFn = gen.destructor,
1706+
};
1707+
}
1708+
1709+
pub fn destructor(self: DestructorCallback, scope: *Scope) void {
1710+
self.destructorFn(self.ptr, scope);
1711+
}
1712+
};
1713+
1714+
// An interface for types that want to have their jsScopeEnd function be
16761715
// called when the call scope ends
16771716
const CallScopeEndCallback = struct {
16781717
ptr: *anyopaque,

0 commit comments

Comments
 (0)