@@ -43,9 +43,17 @@ pub const SingleThreaded = struct {
4343 events_nb : * usize ,
4444 cbk_error : bool = false ,
4545
46- // ctx_id is incremented each time the loop is reset.
47- // All context are
48- ctx_id : u32 = 0 ,
46+ // js_ctx_id is incremented each time the loop is reset for JS.
47+ // All JS callbacks store an initial js_ctx_id and compare before execution.
48+ // If a ctx is outdated, the callback is ignored.
49+ // This is a weak way to cancel all future JS callbacks.
50+ js_ctx_id : u32 = 0 ,
51+
52+ // zig_ctx_id is incremented each time the loop is reset for Zig.
53+ // All Zig callbacks store an initial zig_ctx_id and compare before execution.
54+ // If a ctx is outdated, the callback is ignored.
55+ // This is a weak way to cancel all future Zig callbacks.
56+ zig_ctx_id : u32 = 0 ,
4957
5058 const Self = @This ();
5159 pub const Completion = IO .Completion ;
@@ -120,7 +128,7 @@ pub const SingleThreaded = struct {
120128 const ContextTimeout = struct {
121129 loop : * Self ,
122130 js_cbk : ? JSCallback ,
123- ctx_id : u32 ,
131+ js_ctx_id : u32 ,
124132 };
125133
126134 fn timeoutCallback (
@@ -133,7 +141,7 @@ pub const SingleThreaded = struct {
133141 // If the loop's context id has changed, don't call the js callback
134142 // function. The callback's memory has already be cleaned and the
135143 // events nb reset.
136- if (ctx .ctx_id != ctx .loop .ctx_id ) return ;
144+ if (ctx .js_ctx_id != ctx .loop .js_ctx_id ) return ;
137145
138146 const old_events_nb = ctx .loop .removeEvent ();
139147 if (builtin .is_test ) {
@@ -165,7 +173,7 @@ pub const SingleThreaded = struct {
165173 ctx .* = ContextTimeout {
166174 .loop = self ,
167175 .js_cbk = js_cbk ,
168- .ctx_id = self .ctx_id ,
176+ .js_ctx_id = self .js_ctx_id ,
169177 };
170178 const old_events_nb = self .addEvent ();
171179 self .io .timeout (* ContextTimeout , ctx , timeoutCallback , completion , nanoseconds );
@@ -179,7 +187,7 @@ pub const SingleThreaded = struct {
179187 const ContextCancel = struct {
180188 loop : * Self ,
181189 js_cbk : ? JSCallback ,
182- ctx_id : u32 ,
190+ js_ctx_id : u32 ,
183191 };
184192
185193 fn cancelCallback (
@@ -192,7 +200,7 @@ pub const SingleThreaded = struct {
192200 // If the loop's context id has changed, don't call the js callback
193201 // function. The callback's memory has already be cleaned and the
194202 // events nb reset.
195- if (ctx .ctx_id != ctx .loop .ctx_id ) return ;
203+ if (ctx .js_ctx_id != ctx .loop .js_ctx_id ) return ;
196204
197205 const old_events_nb = ctx .loop .removeEvent ();
198206 if (builtin .is_test ) {
@@ -226,7 +234,7 @@ pub const SingleThreaded = struct {
226234 ctx .* = ContextCancel {
227235 .loop = self ,
228236 .js_cbk = js_cbk ,
229- .ctx_id = self .ctx_id ,
237+ .js_ctx_id = self .js_ctx_id ,
230238 };
231239
232240 const old_events_nb = self .addEvent ();
@@ -241,9 +249,15 @@ pub const SingleThreaded = struct {
241249 self .io .cancel_all ();
242250 }
243251
244- // Reset all existing callbacks.
245- pub fn reset (self : * Self ) void {
246- self .ctx_id += 1 ;
252+ // Reset all existing JS callbacks.
253+ pub fn resetJS (self : * Self ) void {
254+ self .js_ctx_id += 1 ;
255+ self .resetEvents ();
256+ }
257+
258+ // Reset all existing Zig callbacks.
259+ pub fn resetZig (self : * Self ) void {
260+ self .zig_ctx_id += 1 ;
247261 self .resetEvents ();
248262 }
249263
@@ -324,4 +338,77 @@ pub const SingleThreaded = struct {
324338 report ("recv done, remaining events: {d}" , .{old_events_nb - 1 });
325339 }
326340 }
341+
342+ // Zig timeout
343+
344+ const ContextZigTimeout = struct {
345+ loop : * Self ,
346+ zig_ctx_id : u32 ,
347+
348+ context : * anyopaque ,
349+ callback : * const fn (
350+ context : ? * anyopaque ,
351+ ) void ,
352+ };
353+
354+ fn zigTimeoutCallback (
355+ ctx : * ContextZigTimeout ,
356+ completion : * IO.Completion ,
357+ result : IO .TimeoutError ! void ,
358+ ) void {
359+ defer ctx .loop .freeCbk (completion , ctx );
360+
361+ // If the loop's context id has changed, don't call the js callback
362+ // function. The callback's memory has already be cleaned and the
363+ // events nb reset.
364+ if (ctx .zig_ctx_id != ctx .loop .zig_ctx_id ) return ;
365+
366+ // We don't remove event here b/c we don't want the main loop to wait for
367+ // the timeout is done.
368+ // This is mainly due b/c the usage of zigTimeout is used to process
369+ // background tasks.
370+ //_ = ctx.loop.removeEvent();
371+
372+ result catch | err | {
373+ switch (err ) {
374+ error .Canceled = > {},
375+ else = > log .err ("zig timeout callback: {any}" , .{err }),
376+ }
377+ return ;
378+ };
379+
380+ // callback
381+ ctx .callback (ctx .context );
382+ }
383+
384+ // zigTimeout performs a timeout but the callback is a zig function.
385+ pub fn zigTimeout (
386+ self : * Self ,
387+ nanoseconds : u63 ,
388+ comptime Context : type ,
389+ context : Context ,
390+ comptime callback : fn (context : Context ) void ,
391+ ) void {
392+ const completion = self .alloc .create (IO .Completion ) catch unreachable ;
393+ completion .* = undefined ;
394+ const ctxtimeout = self .alloc .create (ContextZigTimeout ) catch unreachable ;
395+ ctxtimeout .* = ContextZigTimeout {
396+ .loop = self ,
397+ .zig_ctx_id = self .zig_ctx_id ,
398+ .context = context ,
399+ .callback = struct {
400+ fn wrapper (ctx : ? * anyopaque ) void {
401+ callback (@ptrCast (@alignCast (ctx )));
402+ }
403+ }.wrapper ,
404+ };
405+
406+ // We don't add event here b/c we don't want the main loop to wait for
407+ // the timeout is done.
408+ // This is mainly due b/c the usage of zigTimeout is used to process
409+ // background tasks.
410+ // _ = self.addEvent();
411+
412+ self .io .timeout (* ContextZigTimeout , ctxtimeout , zigTimeoutCallback , completion , nanoseconds );
413+ }
327414};
0 commit comments