From 88d29c95ac13fc732f8c8e85df630d345409d979 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 9 Apr 2025 09:43:21 +0200 Subject: [PATCH 1/3] loop: add some comment on reset functions --- src/loop.zig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/loop.zig b/src/loop.zig index 74368c1..f2aecc1 100644 --- a/src/loop.zig +++ b/src/loop.zig @@ -33,8 +33,12 @@ const log = std.log.scoped(.loop); pub const SingleThreaded = struct { alloc: std.mem.Allocator, // TODO: unmanaged version ? io: IO, + + // both events_nb are used to track how many callbacks are to be called. + // We use these counters to wait until all the events are finished. js_events_nb: usize, zig_events_nb: usize, + cbk_error: bool = false, // js_ctx_id is incremented each time the loop is reset for JS. @@ -284,6 +288,9 @@ pub const SingleThreaded = struct { self.io.cancel_one(*ContextCancel, ctx, cancelCallback, completion, comp_cancel); } + // cancelAll will cancel all future events. + // The loop will not be usable anymore after cancelAll. + // It is used only during the deinit. fn cancelAll(self: *Self) void { self.resetEvents(.js); self.resetEvents(.zig); @@ -291,14 +298,26 @@ pub const SingleThreaded = struct { } // Reset all existing JS callbacks. + // The existing events will happen and their memory will be cleanup but the + // corresponding callbacks will not be called. pub fn resetJS(self: *Self) void { self.js_ctx_id += 1; self.cancelled.clearRetainingCapacity(); + // We don't call self.resetEvents(.js) intentionnaly. + // Indeed we don't want to prevent the possibility to wait for events. + // The caller may want to wait to have memory correcly cleaned after + // the events happen, even if the callback are ignoreed. } // Reset all existing Zig callbacks. + // The existing events will happen and their memory will be cleanup but the + // corresponding callbacks will not be called. pub fn resetZig(self: *Self) void { self.zig_ctx_id += 1; + // We don't call self.resetEvents(.zig) intentionnaly. + // Indeed we don't want to prevent the possibility to wait for events. + // The caller may want to wait to have memory correcly cleaned after + // the events happen, even if the callback are ignoreed. } // IO callbacks APIs From 2c418d6512cae75dd0794e3a8b3a93a9d09b071a Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 9 Apr 2025 09:58:24 +0200 Subject: [PATCH 2/3] loop: inline cancelAll and remove useless resetEvents --- src/loop.zig | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/loop.zig b/src/loop.zig index f2aecc1..0ce4823 100644 --- a/src/loop.zig +++ b/src/loop.zig @@ -92,7 +92,7 @@ pub const SingleThreaded = struct { }; } if (comptime CANCEL_SUPPORTED) { - self.cancelAll(); + self.io.cancel_all(); } self.io.deinit(); self.cancel_pool.deinit(); @@ -142,9 +142,6 @@ pub const SingleThreaded = struct { fn eventsNb(self: *Self, comptime event: Event) usize { return @atomicLoad(usize, self.eventsPtr(event), .seq_cst); } - fn resetEvents(self: *Self, comptime event: Event) void { - @atomicStore(usize, self.eventsPtr(event), 0, .unordered); - } // JS callbacks APIs // ----------------- @@ -288,25 +285,12 @@ pub const SingleThreaded = struct { self.io.cancel_one(*ContextCancel, ctx, cancelCallback, completion, comp_cancel); } - // cancelAll will cancel all future events. - // The loop will not be usable anymore after cancelAll. - // It is used only during the deinit. - fn cancelAll(self: *Self) void { - self.resetEvents(.js); - self.resetEvents(.zig); - self.io.cancel_all(); - } - // Reset all existing JS callbacks. // The existing events will happen and their memory will be cleanup but the // corresponding callbacks will not be called. pub fn resetJS(self: *Self) void { self.js_ctx_id += 1; self.cancelled.clearRetainingCapacity(); - // We don't call self.resetEvents(.js) intentionnaly. - // Indeed we don't want to prevent the possibility to wait for events. - // The caller may want to wait to have memory correcly cleaned after - // the events happen, even if the callback are ignoreed. } // Reset all existing Zig callbacks. @@ -314,10 +298,6 @@ pub const SingleThreaded = struct { // corresponding callbacks will not be called. pub fn resetZig(self: *Self) void { self.zig_ctx_id += 1; - // We don't call self.resetEvents(.zig) intentionnaly. - // Indeed we don't want to prevent the possibility to wait for events. - // The caller may want to wait to have memory correcly cleaned after - // the events happen, even if the callback are ignoreed. } // IO callbacks APIs From 9a7f1ea564091792d0a2b1d7394481315891d993 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 9 Apr 2025 10:04:47 +0200 Subject: [PATCH 3/3] loop: reset events before waiting during deinit --- src/loop.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/loop.zig b/src/loop.zig index 0ce4823..4b4fbb7 100644 --- a/src/loop.zig +++ b/src/loop.zig @@ -83,6 +83,12 @@ pub const SingleThreaded = struct { } pub fn deinit(self: *Self) void { + // first disable callbacks for existing events. + // We don't want a callback re-create a setTimeout, it could create an + // infinite loop on wait for events. + self.resetJS(); + self.resetZig(); + // run tail events. We do run the tail events to ensure all the // contexts are correcly free. while (self.eventsNb(.js) > 0 or self.eventsNb(.zig) > 0) {