fix(windows): heap-allocate Watch to stabilise OVERLAPPED address

This commit is contained in:
CJ van den Berg 2026-03-14 23:55:10 +01:00
parent f65eb76893
commit 046c304c60
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9

View file

@ -49,7 +49,7 @@ const win32 = struct {
handler: *Handler, handler: *Handler,
iocp: windows.HANDLE, iocp: windows.HANDLE,
thread: ?std.Thread, thread: ?std.Thread,
watches: std.StringHashMapUnmanaged(Watch), watches: std.StringHashMapUnmanaged(*Watch),
watches_mutex: std.Thread.Mutex, watches_mutex: std.Thread.Mutex,
path_types: std.StringHashMapUnmanaged(ObjectType), path_types: std.StringHashMapUnmanaged(ObjectType),
@ -97,9 +97,11 @@ pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
if (self.thread) |t| t.join(); if (self.thread) |t| t.join();
var it = self.watches.iterator(); var it = self.watches.iterator();
while (it.next()) |entry| { while (it.next()) |entry| {
_ = win32.CloseHandle(entry.value_ptr.*.handle); const w = entry.value_ptr.*;
allocator.free(entry.value_ptr.*.path); _ = win32.CloseHandle(w.handle);
allocator.free(entry.value_ptr.*.buf); allocator.free(w.path);
allocator.free(w.buf);
allocator.destroy(w);
} }
self.watches.deinit(allocator); self.watches.deinit(allocator);
var pt_it = self.path_types.iterator(); var pt_it = self.path_types.iterator();
@ -116,7 +118,7 @@ pub fn arm(self: *@This(), allocator: std.mem.Allocator) (error{AlreadyArmed} ||
fn thread_fn( fn thread_fn(
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
iocp: windows.HANDLE, iocp: windows.HANDLE,
watches: *std.StringHashMapUnmanaged(Watch), watches: *std.StringHashMapUnmanaged(*Watch),
watches_mutex: *std.Thread.Mutex, watches_mutex: *std.Thread.Mutex,
path_types: *std.StringHashMapUnmanaged(ObjectType), path_types: *std.StringHashMapUnmanaged(ObjectType),
handler: *Handler, handler: *Handler,
@ -132,7 +134,7 @@ fn thread_fn(
watches_mutex.lock(); watches_mutex.lock();
var it = watches.iterator(); var it = watches.iterator();
while (it.next()) |entry| { while (it.next()) |entry| {
const w = entry.value_ptr; const w = entry.value_ptr.*;
if (w.handle != triggered_handle) continue; if (w.handle != triggered_handle) continue;
if (bytes > 0) { if (bytes > 0) {
var offset: usize = 0; var offset: usize = 0;
@ -236,23 +238,24 @@ pub fn add_watch(self: *@This(), allocator: std.mem.Allocator, path: []const u8)
errdefer allocator.free(buf); errdefer allocator.free(buf);
const owned_path = try allocator.dupe(u8, path); const owned_path = try allocator.dupe(u8, path);
errdefer allocator.free(owned_path); errdefer allocator.free(owned_path);
var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED); // Heap-allocate Watch so its address (and &w.overlapped) is stable even
if (win32.ReadDirectoryChangesW(handle, buf.ptr, buf_size, 1, notify_filter, null, &overlapped, null) == 0) // if the watches map is resized by a concurrent add_watch call.
const w = try allocator.create(Watch);
errdefer allocator.destroy(w);
w.* = .{ .handle = handle, .buf = buf, .overlapped = std.mem.zeroes(windows.OVERLAPPED), .path = owned_path };
if (win32.ReadDirectoryChangesW(handle, buf.ptr, buf_size, 1, notify_filter, null, &w.overlapped, null) == 0)
return error.WatchFailed; return error.WatchFailed;
try self.watches.put(allocator, owned_path, .{ try self.watches.put(allocator, owned_path, w);
.handle = handle,
.buf = buf,
.overlapped = overlapped,
.path = owned_path,
});
} }
pub fn remove_watch(self: *@This(), allocator: std.mem.Allocator, path: []const u8) void { pub fn remove_watch(self: *@This(), allocator: std.mem.Allocator, path: []const u8) void {
self.watches_mutex.lock(); self.watches_mutex.lock();
defer self.watches_mutex.unlock(); defer self.watches_mutex.unlock();
if (self.watches.fetchRemove(path)) |entry| { if (self.watches.fetchRemove(path)) |entry| {
_ = win32.CloseHandle(entry.value.handle); const w = entry.value;
allocator.free(entry.value.path); _ = win32.CloseHandle(w.handle);
allocator.free(entry.value.buf); allocator.free(w.path);
allocator.free(w.buf);
allocator.destroy(w);
} }
} }