From 69b705f8ea9c97bba4a94ca511a7cf8cde096687 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Mon, 13 Apr 2026 13:24:30 +0200 Subject: [PATCH] build(zig-0.16): port windows backend to zig-0.16 --- src/backend/Windows.zig | 55 +++++++++++++++++++++++++++++------------ src/main.zig | 10 ++++---- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/backend/Windows.zig b/src/backend/Windows.zig index fa1c00c..5abdf74 100644 --- a/src/backend/Windows.zig +++ b/src/backend/Windows.zig @@ -13,6 +13,23 @@ pub const emits_subtree_created_on_movein = true; const windows = std.os.windows; +// Types removed from std.os.windows in zig-0.16. +const OVERLAPPED = extern struct { + Internal: windows.ULONG_PTR = 0, + InternalHigh: windows.ULONG_PTR = 0, + Offset: windows.DWORD = 0, + OffsetHigh: windows.DWORD = 0, + hEvent: ?windows.HANDLE = null, +}; + +// Constants removed from std.os.windows in zig-0.16. +const GENERIC_READ: windows.DWORD = 0x80000000; +const FILE_SHARE_READ: windows.DWORD = 0x00000001; +const FILE_SHARE_WRITE: windows.DWORD = 0x00000002; +const FILE_SHARE_DELETE: windows.DWORD = 0x00000004; +const OPEN_EXISTING: windows.DWORD = 3; +const INFINITE: windows.DWORD = 0xFFFFFFFF; + const win32 = struct { pub extern "kernel32" fn CloseHandle(hObject: windows.HANDLE) callconv(.winapi) windows.BOOL; pub extern "kernel32" fn ReadDirectoryChangesW( @@ -22,14 +39,14 @@ const win32 = struct { bWatchSubtree: windows.BOOL, dwNotifyFilter: windows.DWORD, lpBytesReturned: ?*windows.DWORD, - lpOverlapped: ?*windows.OVERLAPPED, + lpOverlapped: ?*OVERLAPPED, lpCompletionRoutine: ?*anyopaque, ) callconv(.winapi) windows.BOOL; pub extern "kernel32" fn GetQueuedCompletionStatus( CompletionPort: windows.HANDLE, lpNumberOfBytesTransferred: *windows.DWORD, lpCompletionKey: *windows.ULONG_PTR, - lpOverlapped: *?*windows.OVERLAPPED, + lpOverlapped: *?*OVERLAPPED, dwMilliseconds: windows.DWORD, ) callconv(.winapi) windows.BOOL; pub extern "kernel32" fn CreateFileW( @@ -45,9 +62,15 @@ const win32 = struct { CompletionPort: windows.HANDLE, dwNumberOfBytesTransferred: windows.DWORD, dwCompletionKey: windows.ULONG_PTR, - lpOverlapped: ?*windows.OVERLAPPED, + lpOverlapped: ?*OVERLAPPED, ) callconv(.winapi) windows.BOOL; pub extern "kernel32" fn GetFileAttributesW(lpFileName: [*:0]const windows.WCHAR) callconv(.winapi) windows.DWORD; + pub extern "kernel32" fn CreateIoCompletionPort( + FileHandle: windows.HANDLE, + ExistingCompletionPort: ?windows.HANDLE, + CompletionKey: windows.ULONG_PTR, + NumberOfConcurrentThreads: windows.DWORD, + ) callconv(.winapi) ?windows.HANDLE; }; handler: *Handler, @@ -64,7 +87,7 @@ const SHUTDOWN_KEY: windows.ULONG_PTR = 0; const Watch = struct { handle: windows.HANDLE, buf: Buf, - overlapped: windows.OVERLAPPED, + overlapped: OVERLAPPED, path: []u8, // owned }; @@ -92,7 +115,7 @@ const notify_filter: windows.DWORD = 0x00000040; // FILE_NOTIFY_CHANGE_CREATION pub fn init(io: std.Io, handler: *Handler) !@This() { - const iocp = try windows.CreateIoCompletionPort(windows.INVALID_HANDLE_VALUE, null, 0, 1); + const iocp = win32.CreateIoCompletionPort(windows.INVALID_HANDLE_VALUE, null, 0, 1) orelse return error.SystemResources; return .{ .handler = handler, .io = io, .iocp = iocp, .thread = null, .watches = .empty, .watches_mutex = std.Io.Mutex.init, .path_types = .empty }; } @@ -131,11 +154,11 @@ fn thread_fn( ) void { var bytes: windows.DWORD = 0; var key: windows.ULONG_PTR = 0; - var overlapped_ptr: ?*windows.OVERLAPPED = null; + var overlapped_ptr: ?*OVERLAPPED = null; while (true) { // Block indefinitely until IOCP has a completion or shutdown signal. - const ok = win32.GetQueuedCompletionStatus(iocp, &bytes, &key, &overlapped_ptr, windows.INFINITE); - if (ok == 0 or key == SHUTDOWN_KEY) return; + const ok = win32.GetQueuedCompletionStatus(iocp, &bytes, &key, &overlapped_ptr, INFINITE); + if (ok == .FALSE or key == SHUTDOWN_KEY) return; const triggered_handle: windows.HANDLE = @ptrFromInt(key); watches_mutex.lockUncancelable(io); var it = watches.iterator(); @@ -303,8 +326,8 @@ fn thread_fn( } } // Re-arm ReadDirectoryChangesW for the next batch. - w.overlapped = std.mem.zeroes(windows.OVERLAPPED); - if (win32.ReadDirectoryChangesW(w.handle, w.buf.ptr, buf_size, 1, notify_filter, null, &w.overlapped, null) == 0) + w.overlapped = std.mem.zeroes(OVERLAPPED); + if (win32.ReadDirectoryChangesW(w.handle, w.buf.ptr, buf_size, .TRUE, notify_filter, null, &w.overlapped, null) == .FALSE) std.log.err("nightwatch: ReadDirectoryChangesW re-arm failed for {s}, future events lost", .{entry.key_ptr.*}); break; } @@ -320,16 +343,16 @@ pub fn add_watch(self: *@This(), allocator: std.mem.Allocator, path: []const u8) defer allocator.free(path_w); const handle = win32.CreateFileW( path_w, - windows.GENERIC_READ, - windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_DELETE, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, null, - windows.OPEN_EXISTING, + OPEN_EXISTING, 0x02000000 | 0x40000000, // FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED null, ); if (handle == windows.INVALID_HANDLE_VALUE) return error.WatchFailed; errdefer _ = win32.CloseHandle(handle); - _ = windows.CreateIoCompletionPort(handle, self.iocp, @intFromPtr(handle), 0) catch return error.WatchFailed; + _ = win32.CreateIoCompletionPort(handle, self.iocp, @intFromPtr(handle), 0) orelse return error.WatchFailed; const buf = try allocator.alignedAlloc(u8, .fromByteUnits(4), buf_size); errdefer allocator.free(buf); const owned_path = try allocator.dupe(u8, path); @@ -338,8 +361,8 @@ pub fn add_watch(self: *@This(), allocator: std.mem.Allocator, path: []const u8) // 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) + w.* = .{ .handle = handle, .buf = buf, .overlapped = std.mem.zeroes(OVERLAPPED), .path = owned_path }; + if (win32.ReadDirectoryChangesW(handle, buf.ptr, buf_size, .TRUE, notify_filter, null, &w.overlapped, null) == .FALSE) return error.WatchFailed; try self.watches.put(allocator, owned_path, w); // Seed path_types with pre-existing entries so delete/rename events for diff --git a/src/main.zig b/src/main.zig index 120eb0d..7cf4a2d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -133,19 +133,19 @@ var win_shutdown = std.atomic.Value(bool).init(false); fn win_ctrl_handler(ctrl_type: std.os.windows.DWORD) callconv(.winapi) std.os.windows.BOOL { _ = ctrl_type; win_shutdown.store(true, .release); - return std.os.windows.TRUE; + return .TRUE; } -fn run_windows() void { +fn run_windows(io: std.Io) void { const SetConsoleCtrlHandler = struct { extern "kernel32" fn SetConsoleCtrlHandler( HandlerRoutine: ?*const fn (std.os.windows.DWORD) callconv(.winapi) std.os.windows.BOOL, Add: std.os.windows.BOOL, ) callconv(.winapi) std.os.windows.BOOL; }.SetConsoleCtrlHandler; - _ = SetConsoleCtrlHandler(win_ctrl_handler, std.os.windows.TRUE); + _ = SetConsoleCtrlHandler(win_ctrl_handler, .TRUE); while (!win_shutdown.load(.acquire)) { - std.Thread.sleep(50 * std.time.ns_per_ms); + std.Io.sleep(io, std.Io.Duration.fromMilliseconds(50), .awake) catch {}; } } @@ -309,7 +309,7 @@ pub fn main(init: std.process.Init) !void { if (Watcher.interface_type == .polling) { try run_linux(&watcher); } else if (builtin.os.tag == .windows) { - run_windows(); + run_windows(init.io); } else if (is_posix) { run_posix(); }