authorgravatar for rb.lymn@gmail.comRobbie Lyman <rb.lymn@gmail.com> 2026-08-03 14:35:39-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-05 02:55:26+02:00
logce96dcceab7731f63534425f1d25bfe9be014847
treeee6bc33a44874fcca256632c8edbbae170b2c6a8
parent9688e6e6208910b3e8dce5a6b93ea743fd46a575

fix(Io.Dispatch): node = .{}, mutex deinit, wrong canceled

In most of the file `node: std.DoublyLinkedList.Node` is default-initialized as `.{}` rather than `undefined`. I don't believe this resolves any crashes for me, but it seems incorrect. If `backing_allocator_needs_mutex` is `true` and the backing allocator has been used nontrivially; for example in creating a `Group`, the current `Dispatch.deinit()` code segfaults. Since the error also goes away when `backing_allocator_needs_mutex` is false, I believe remembering to call `deinit()` on the mutex is the fix. Canceling a sleep call currently crashes because of a soundness issue: The `@fieldParentPtr` type punning in `SleepWaiter.canceled` cannot be replaced by `Futex.Waiter.canceled`. For me, the failure winds up being in `waiter.remove()` meeting a node whose `prev` field is mostly `0xaa`, which made this one a real pain to fix.

1 files changed, 3 insertions(+), 2 deletions(-)

lib/std/Io/Dispatch.zig+3-2
...@@ -580,6 +580,7 @@ pub fn deinit(ev: *Evented) void {...@@ -580,6 +580,7 @@ pub fn deinit(ev: *Evented) void {
580 ev.stderr_mutex.deinit();580 ev.stderr_mutex.deinit();
581 for (&ev.futexes) |*futex| futex.deinit();581 for (&ev.futexes) |*futex| futex.deinit();
582 ev.exit_semaphore.as_object().release();582 ev.exit_semaphore.as_object().release();
583 ev.backing_allocator_mutex.deinit();
583 ev.backing_allocator.free(ev.main_loop_stack[0..main_loop_stack_size]);584 ev.backing_allocator.free(ev.main_loop_stack[0..main_loop_stack_size]);
584 ev.queue.as_object().release();585 ev.queue.as_object().release();
585}586}
...@@ -825,7 +826,7 @@ const Mutex = struct {...@@ -825,7 +826,7 @@ const Mutex = struct {
825 sleeper: Sleeper = undefined,826 sleeper: Sleeper = undefined,
826 cancelable: Cancelable,827 cancelable: Cancelable,
827 mutex: *Mutex,828 mutex: *Mutex,
828 node: std.DoublyLinkedList.Node = undefined,829 node: std.DoublyLinkedList.Node = .{},
829830
830 fn add(context: ?*anyopaque) callconv(.c) void {831 fn add(context: ?*anyopaque) callconv(.c) void {
831 const waiter: *Waiter = @ptrCast(@alignCast(context));832 const waiter: *Waiter = @ptrCast(@alignCast(context));
...@@ -4714,7 +4715,7 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.Cancelable!void {...@@ -4714,7 +4715,7 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.Cancelable!void {
4714 return ev.yield(.{ .after = ev.timeFromTimeout(timeout) });4715 return ev.yield(.{ .after = ev.timeFromTimeout(timeout) });
4715 };4716 };
4716 var waiter: SleepWaiter = .{4717 var waiter: SleepWaiter = .{
4717 .cancelable = .{ .queue = queue, .cancel = &Futex.Waiter.canceled },4718 .cancelable = .{ .queue = queue, .cancel = &SleepWaiter.canceled },
4718 .timer = timer,4719 .timer = timer,
4719 };4720 };
4720 timer.as_object().set_context(&waiter);4721 timer.as_object().set_context(&waiter);