build(zig-0.16): port windows backend to zig-0.16

This commit is contained in:
CJ van den Berg 2026-04-13 13:24:30 +02:00
parent 5c863fbb4a
commit 69b705f8ea
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
2 changed files with 44 additions and 21 deletions

View file

@ -13,6 +13,23 @@ pub const emits_subtree_created_on_movein = true;
const windows = std.os.windows; 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 { const win32 = struct {
pub extern "kernel32" fn CloseHandle(hObject: windows.HANDLE) callconv(.winapi) windows.BOOL; pub extern "kernel32" fn CloseHandle(hObject: windows.HANDLE) callconv(.winapi) windows.BOOL;
pub extern "kernel32" fn ReadDirectoryChangesW( pub extern "kernel32" fn ReadDirectoryChangesW(
@ -22,14 +39,14 @@ const win32 = struct {
bWatchSubtree: windows.BOOL, bWatchSubtree: windows.BOOL,
dwNotifyFilter: windows.DWORD, dwNotifyFilter: windows.DWORD,
lpBytesReturned: ?*windows.DWORD, lpBytesReturned: ?*windows.DWORD,
lpOverlapped: ?*windows.OVERLAPPED, lpOverlapped: ?*OVERLAPPED,
lpCompletionRoutine: ?*anyopaque, lpCompletionRoutine: ?*anyopaque,
) callconv(.winapi) windows.BOOL; ) callconv(.winapi) windows.BOOL;
pub extern "kernel32" fn GetQueuedCompletionStatus( pub extern "kernel32" fn GetQueuedCompletionStatus(
CompletionPort: windows.HANDLE, CompletionPort: windows.HANDLE,
lpNumberOfBytesTransferred: *windows.DWORD, lpNumberOfBytesTransferred: *windows.DWORD,
lpCompletionKey: *windows.ULONG_PTR, lpCompletionKey: *windows.ULONG_PTR,
lpOverlapped: *?*windows.OVERLAPPED, lpOverlapped: *?*OVERLAPPED,
dwMilliseconds: windows.DWORD, dwMilliseconds: windows.DWORD,
) callconv(.winapi) windows.BOOL; ) callconv(.winapi) windows.BOOL;
pub extern "kernel32" fn CreateFileW( pub extern "kernel32" fn CreateFileW(
@ -45,9 +62,15 @@ const win32 = struct {
CompletionPort: windows.HANDLE, CompletionPort: windows.HANDLE,
dwNumberOfBytesTransferred: windows.DWORD, dwNumberOfBytesTransferred: windows.DWORD,
dwCompletionKey: windows.ULONG_PTR, dwCompletionKey: windows.ULONG_PTR,
lpOverlapped: ?*windows.OVERLAPPED, lpOverlapped: ?*OVERLAPPED,
) callconv(.winapi) windows.BOOL; ) callconv(.winapi) windows.BOOL;
pub extern "kernel32" fn GetFileAttributesW(lpFileName: [*:0]const windows.WCHAR) callconv(.winapi) windows.DWORD; 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, handler: *Handler,
@ -64,7 +87,7 @@ const SHUTDOWN_KEY: windows.ULONG_PTR = 0;
const Watch = struct { const Watch = struct {
handle: windows.HANDLE, handle: windows.HANDLE,
buf: Buf, buf: Buf,
overlapped: windows.OVERLAPPED, overlapped: OVERLAPPED,
path: []u8, // owned path: []u8, // owned
}; };
@ -92,7 +115,7 @@ const notify_filter: windows.DWORD =
0x00000040; // FILE_NOTIFY_CHANGE_CREATION 0x00000040; // FILE_NOTIFY_CHANGE_CREATION
pub fn init(io: std.Io, handler: *Handler) !@This() { 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 }; 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 { ) void {
var bytes: windows.DWORD = 0; var bytes: windows.DWORD = 0;
var key: windows.ULONG_PTR = 0; var key: windows.ULONG_PTR = 0;
var overlapped_ptr: ?*windows.OVERLAPPED = null; var overlapped_ptr: ?*OVERLAPPED = null;
while (true) { while (true) {
// Block indefinitely until IOCP has a completion or shutdown signal. // Block indefinitely until IOCP has a completion or shutdown signal.
const ok = win32.GetQueuedCompletionStatus(iocp, &bytes, &key, &overlapped_ptr, windows.INFINITE); const ok = win32.GetQueuedCompletionStatus(iocp, &bytes, &key, &overlapped_ptr, INFINITE);
if (ok == 0 or key == SHUTDOWN_KEY) return; if (ok == .FALSE or key == SHUTDOWN_KEY) return;
const triggered_handle: windows.HANDLE = @ptrFromInt(key); const triggered_handle: windows.HANDLE = @ptrFromInt(key);
watches_mutex.lockUncancelable(io); watches_mutex.lockUncancelable(io);
var it = watches.iterator(); var it = watches.iterator();
@ -303,8 +326,8 @@ fn thread_fn(
} }
} }
// Re-arm ReadDirectoryChangesW for the next batch. // Re-arm ReadDirectoryChangesW for the next batch.
w.overlapped = std.mem.zeroes(windows.OVERLAPPED); w.overlapped = std.mem.zeroes(OVERLAPPED);
if (win32.ReadDirectoryChangesW(w.handle, w.buf.ptr, buf_size, 1, notify_filter, null, &w.overlapped, null) == 0) 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.*}); std.log.err("nightwatch: ReadDirectoryChangesW re-arm failed for {s}, future events lost", .{entry.key_ptr.*});
break; break;
} }
@ -320,16 +343,16 @@ pub fn add_watch(self: *@This(), allocator: std.mem.Allocator, path: []const u8)
defer allocator.free(path_w); defer allocator.free(path_w);
const handle = win32.CreateFileW( const handle = win32.CreateFileW(
path_w, path_w,
windows.GENERIC_READ, GENERIC_READ,
windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
null, null,
windows.OPEN_EXISTING, OPEN_EXISTING,
0x02000000 | 0x40000000, // FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED 0x02000000 | 0x40000000, // FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED
null, null,
); );
if (handle == windows.INVALID_HANDLE_VALUE) return error.WatchFailed; if (handle == windows.INVALID_HANDLE_VALUE) return error.WatchFailed;
errdefer _ = win32.CloseHandle(handle); 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); const buf = try allocator.alignedAlloc(u8, .fromByteUnits(4), buf_size);
errdefer allocator.free(buf); errdefer allocator.free(buf);
const owned_path = try allocator.dupe(u8, path); 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. // if the watches map is resized by a concurrent add_watch call.
const w = try allocator.create(Watch); const w = try allocator.create(Watch);
errdefer allocator.destroy(w); errdefer allocator.destroy(w);
w.* = .{ .handle = handle, .buf = buf, .overlapped = std.mem.zeroes(windows.OVERLAPPED), .path = owned_path }; w.* = .{ .handle = handle, .buf = buf, .overlapped = std.mem.zeroes(OVERLAPPED), .path = owned_path };
if (win32.ReadDirectoryChangesW(handle, buf.ptr, buf_size, 1, notify_filter, null, &w.overlapped, null) == 0) if (win32.ReadDirectoryChangesW(handle, buf.ptr, buf_size, .TRUE, notify_filter, null, &w.overlapped, null) == .FALSE)
return error.WatchFailed; return error.WatchFailed;
try self.watches.put(allocator, owned_path, w); try self.watches.put(allocator, owned_path, w);
// Seed path_types with pre-existing entries so delete/rename events for // Seed path_types with pre-existing entries so delete/rename events for

View file

@ -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 { fn win_ctrl_handler(ctrl_type: std.os.windows.DWORD) callconv(.winapi) std.os.windows.BOOL {
_ = ctrl_type; _ = ctrl_type;
win_shutdown.store(true, .release); 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 { const SetConsoleCtrlHandler = struct {
extern "kernel32" fn SetConsoleCtrlHandler( extern "kernel32" fn SetConsoleCtrlHandler(
HandlerRoutine: ?*const fn (std.os.windows.DWORD) callconv(.winapi) std.os.windows.BOOL, HandlerRoutine: ?*const fn (std.os.windows.DWORD) callconv(.winapi) std.os.windows.BOOL,
Add: std.os.windows.BOOL, Add: std.os.windows.BOOL,
) callconv(.winapi) std.os.windows.BOOL; ) callconv(.winapi) std.os.windows.BOOL;
}.SetConsoleCtrlHandler; }.SetConsoleCtrlHandler;
_ = SetConsoleCtrlHandler(win_ctrl_handler, std.os.windows.TRUE); _ = SetConsoleCtrlHandler(win_ctrl_handler, .TRUE);
while (!win_shutdown.load(.acquire)) { 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) { if (Watcher.interface_type == .polling) {
try run_linux(&watcher); try run_linux(&watcher);
} else if (builtin.os.tag == .windows) { } else if (builtin.os.tag == .windows) {
run_windows(); run_windows(init.io);
} else if (is_posix) { } else if (is_posix) {
run_posix(); run_posix();
} }