authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-21 07:10:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:50-07:00
logd257b1337a1b0cbe5b2694518017216aa1d2ef1a
tree11bdf94a0a5a262297d371e7190c83c43dc30c57
parentaadd8d4a3efd8912c47b8ea1bbd4c3a6649c3e5d

std.Io.Threaded: fix compilation failures on Windows


4 files changed, 183 insertions(+), 141 deletions(-)

lib/std/Io/Threaded.zig+172-121
......@@ -220,8 +220,14 @@ pub fn io(t: *Threaded) Io {
220220 .fileClose = fileClose,
221221 .fileWriteStreaming = fileWriteStreaming,
222222 .fileWritePositional = fileWritePositional,
223 .fileReadStreaming = fileReadStreaming,
224 .fileReadPositional = fileReadPositional,
223 .fileReadStreaming = switch (builtin.os.tag) {
224 .windows => fileReadStreamingWindows,
225 else => fileReadStreamingPosix,
226 },
227 .fileReadPositional = switch (builtin.os.tag) {
228 .windows => fileReadPositionalWindows,
229 else => fileReadPositionalPosix,
230 },
225231 .fileSeekBy = fileSeekBy,
226232 .fileSeekTo = fileSeekTo,
227233 .openSelfExe = openSelfExe,
......@@ -258,15 +264,21 @@ pub fn io(t: *Threaded) Io {
258264 .netConnectUnix = netConnectUnix,
259265 .netClose = netClose,
260266 .netRead = switch (builtin.os.tag) {
261 .windows => @panic("TODO"),
267 .windows => netReadWindows,
262268 else => netReadPosix,
263269 },
264270 .netWrite = switch (builtin.os.tag) {
265 .windows => @panic("TODO"),
271 .windows => netWriteWindows,
266272 else => netWritePosix,
267273 },
268 .netSend = netSend,
269 .netReceive = netReceive,
274 .netSend = switch (builtin.os.tag) {
275 .windows => netSendWindows,
276 else => netSendPosix,
277 },
278 .netReceive = switch (builtin.os.tag) {
279 .windows => netReceiveWindows,
280 else => netReceivePosix,
281 },
270282 .netInterfaceNameResolve = netInterfaceNameResolve,
271283 .netInterfaceName = netInterfaceName,
272284 .netLookup = netLookup,
......@@ -284,6 +296,10 @@ const have_futex = switch (builtin.cpu.arch) {
284296 .wasm32, .wasm64 => builtin.cpu.has(.wasm, .atomics),
285297 else => true,
286298};
299const have_preadv = switch (native_os) {
300 .windows, .haiku, .serenity => false, // 💩💩💩
301 else => true,
302};
287303
288304const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat;
289305const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat;
......@@ -1899,12 +1915,19 @@ fn dirOpenFileWindows(
18991915 flags: Io.File.OpenFlags,
19001916) Io.File.OpenError!Io.File {
19011917 const t: *Threaded = @ptrCast(@alignCast(userdata));
1902 try t.checkCancel();
1903
1904 const w = windows;
1905 const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path);
1918 const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
19061919 const sub_path_w = sub_path_w_array.span();
1920 return dirOpenFileWindowsInner(t, dir, sub_path_w, flags);
1921}
19071922
1923fn dirOpenFileWindowsInner(
1924 t: *Threaded,
1925 dir: Io.Dir,
1926 sub_path_w: [:0]const u16,
1927 flags: Io.File.OpenFlags,
1928) Io.File.OpenError!Io.File {
1929 try t.checkCancel();
1930 const w = windows;
19081931 const handle = try w.OpenFile(sub_path_w, .{
19091932 .dir = dir.handle,
19101933 .access_mask = w.SYNCHRONIZE |
......@@ -2247,47 +2270,9 @@ fn fileClose(userdata: ?*anyopaque, file: Io.File) void {
22472270 posix.close(file.handle);
22482271}
22492272
2250fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.ReadStreamingError!usize {
2273fn fileReadStreamingPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.ReadStreamingError!usize {
22512274 const t: *Threaded = @ptrCast(@alignCast(userdata));
22522275
2253 if (is_windows) {
2254 const DWORD = windows.DWORD;
2255 var index: usize = 0;
2256 var truncate: usize = 0;
2257 var total: usize = 0;
2258 while (index < data.len) {
2259 try t.checkCancel();
2260 {
2261 const untruncated = data[index];
2262 data[index] = untruncated[truncate..];
2263 defer data[index] = untruncated;
2264 const buffer = data[index..];
2265 const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
2266 var n: DWORD = undefined;
2267 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) == 0) {
2268 switch (windows.GetLastError()) {
2269 .IO_PENDING => |err| return windows.statusBug(err),
2270 .OPERATION_ABORTED => continue,
2271 .BROKEN_PIPE => return 0,
2272 .HANDLE_EOF => return 0,
2273 .NETNAME_DELETED => return error.ConnectionResetByPeer,
2274 .LOCK_VIOLATION => return error.LockViolation,
2275 .ACCESS_DENIED => return error.AccessDenied,
2276 .INVALID_HANDLE => return error.NotOpenForReading,
2277 else => |err| return windows.unexpectedError(err),
2278 }
2279 }
2280 total += n;
2281 truncate += n;
2282 }
2283 while (index < data.len and truncate >= data[index].len) {
2284 truncate -= data[index].len;
2285 index += 1;
2286 }
2287 }
2288 return total;
2289 }
2290
22912276 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
22922277 var i: usize = 0;
22932278 for (data) |buf| {
......@@ -2348,70 +2333,37 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
23482333 }
23492334}
23502335
2351fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize {
2336fn fileReadStreamingWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.ReadStreamingError!usize {
23522337 const t: *Threaded = @ptrCast(@alignCast(userdata));
2338 try t.checkCancel();
23532339
2354 if (is_windows) {
2355 const DWORD = windows.DWORD;
2356 const OVERLAPPED = windows.OVERLAPPED;
2357 var index: usize = 0;
2358 var truncate: usize = 0;
2359 var total: usize = 0;
2360 while (true) {
2361 try t.checkCancel();
2362 {
2363 const untruncated = data[index];
2364 data[index] = untruncated[truncate..];
2365 defer data[index] = untruncated;
2366 const buffer = data[index..];
2367 const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
2368 var n: DWORD = undefined;
2369 var overlapped_data: OVERLAPPED = undefined;
2370 const overlapped: ?*OVERLAPPED = if (offset) |off| blk: {
2371 overlapped_data = .{
2372 .Internal = 0,
2373 .InternalHigh = 0,
2374 .DUMMYUNIONNAME = .{
2375 .DUMMYSTRUCTNAME = .{
2376 .Offset = @as(u32, @truncate(off)),
2377 .OffsetHigh = @as(u32, @truncate(off >> 32)),
2378 },
2379 },
2380 .hEvent = null,
2381 };
2382 break :blk &overlapped_data;
2383 } else null;
2384 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, overlapped) == 0) {
2385 switch (windows.GetLastError()) {
2386 .IO_PENDING => |err| return windows.statusBug(err),
2387 .OPERATION_ABORTED => continue,
2388 .BROKEN_PIPE => return 0,
2389 .HANDLE_EOF => return 0,
2390 .NETNAME_DELETED => return error.ConnectionResetByPeer,
2391 .LOCK_VIOLATION => return error.LockViolation,
2392 .ACCESS_DENIED => return error.AccessDenied,
2393 .INVALID_HANDLE => return error.NotOpenForReading,
2394 else => |err| return windows.unexpectedError(err),
2395 }
2396 }
2397 total += n;
2398 truncate += n;
2399 }
2400 while (index < data.len and truncate >= data[index].len) {
2401 truncate -= data[index].len;
2402 index += 1;
2403 }
2340 const DWORD = windows.DWORD;
2341 var index: usize = 0;
2342 while (data[index].len == 0) index += 1;
2343
2344 const buffer = data[index];
2345 const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
2346 var n: DWORD = undefined;
2347 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) == 0) {
2348 switch (windows.GetLastError()) {
2349 .IO_PENDING => |err| return windows.errorBug(err),
2350 .OPERATION_ABORTED => return error.Canceled,
2351 .BROKEN_PIPE => return 0,
2352 .HANDLE_EOF => return 0,
2353 .NETNAME_DELETED => return error.ConnectionResetByPeer,
2354 .LOCK_VIOLATION => return error.LockViolation,
2355 .ACCESS_DENIED => return error.AccessDenied,
2356 .INVALID_HANDLE => return error.NotOpenForReading,
2357 else => |err| return windows.unexpectedError(err),
24042358 }
2405 return total;
24062359 }
2360 return n;
2361}
24072362
2408 const have_pread_but_not_preadv = switch (native_os) {
2409 .windows, .haiku, .serenity => true,
2410 else => false,
2411 };
2412 if (have_pread_but_not_preadv) {
2413 @compileError("TODO");
2414 }
2363fn fileReadPositionalPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize {
2364 const t: *Threaded = @ptrCast(@alignCast(userdata));
2365
2366 if (!have_preadv) @compileError("TODO");
24152367
24162368 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
24172369 var i: usize = 0;
......@@ -2480,6 +2432,48 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
24802432 }
24812433}
24822434
2435fn fileReadPositionalWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize {
2436 const t: *Threaded = @ptrCast(@alignCast(userdata));
2437 try t.checkCancel();
2438
2439 const DWORD = windows.DWORD;
2440 const OVERLAPPED = windows.OVERLAPPED;
2441
2442 var index: usize = 0;
2443 while (data[index].len == 0) index += 1;
2444
2445 const buffer = data[index];
2446 const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
2447 var n: DWORD = undefined;
2448 var overlapped: OVERLAPPED = .{
2449 .Internal = 0,
2450 .InternalHigh = 0,
2451 .DUMMYUNIONNAME = .{
2452 .DUMMYSTRUCTNAME = .{
2453 .Offset = @as(u32, @truncate(offset)),
2454 .OffsetHigh = @as(u32, @truncate(offset >> 32)),
2455 },
2456 },
2457 .hEvent = null,
2458 };
2459
2460 if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) == 0) {
2461 switch (windows.GetLastError()) {
2462 .IO_PENDING => |err| return windows.errorBug(err),
2463 .OPERATION_ABORTED => return error.Canceled,
2464 .BROKEN_PIPE => return 0,
2465 .HANDLE_EOF => return 0,
2466 .NETNAME_DELETED => return error.ConnectionResetByPeer,
2467 .LOCK_VIOLATION => return error.LockViolation,
2468 .ACCESS_DENIED => return error.AccessDenied,
2469 .INVALID_HANDLE => return error.NotOpenForReading,
2470 else => |err| return windows.unexpectedError(err),
2471 }
2472 }
2473
2474 return n;
2475}
2476
24832477fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekError!void {
24842478 const t: *Threaded = @ptrCast(@alignCast(userdata));
24852479 try t.checkCancel();
......@@ -2563,11 +2557,9 @@ fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelf
25632557 // the file, we can let the openFileW call follow the symlink for us.
25642558 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
25652559 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
2566 const prefixed_path_w_array = try windows.wToPrefixedFileW(null, image_path_name);
2567 const prefixed_path_w = prefixed_path_w_array.span();
25682560 const cwd_handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle;
25692561
2570 return dirOpenFileWindows(t, .{ .handle = cwd_handle }, prefixed_path_w, flags);
2562 return dirOpenFileWindowsInner(t, .{ .handle = cwd_handle }, image_path_name, flags);
25712563 }
25722564 @panic("TODO");
25732565}
......@@ -3493,7 +3485,16 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.
34933485 }
34943486}
34953487
3496fn netSend(
3488fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
3489 if (!have_networking) return .{ error.NetworkDown, 0 };
3490 const t: *Threaded = @ptrCast(@alignCast(userdata));
3491 _ = t;
3492 _ = handle;
3493 _ = data;
3494 @panic("TODO");
3495}
3496
3497fn netSendPosix(
34973498 userdata: ?*anyopaque,
34983499 handle: net.Socket.Handle,
34993500 messages: []net.OutgoingMessage,
......@@ -3504,9 +3505,9 @@ fn netSend(
35043505
35053506 const posix_flags: u32 =
35063507 @as(u32, if (@hasDecl(posix.MSG, "CONFIRM") and flags.confirm) posix.MSG.CONFIRM else 0) |
3507 @as(u32, if (flags.dont_route) posix.MSG.DONTROUTE else 0) |
3508 @as(u32, if (flags.eor) posix.MSG.EOR else 0) |
3509 @as(u32, if (flags.oob) posix.MSG.OOB else 0) |
3508 @as(u32, if (@hasDecl(posix.MSG, "DONTROUTE") and flags.dont_route) posix.MSG.DONTROUTE else 0) |
3509 @as(u32, if (@hasDecl(posix.MSG, "EOR") and flags.eor) posix.MSG.EOR else 0) |
3510 @as(u32, if (@hasDecl(posix.MSG, "OOB") and flags.oob) posix.MSG.OOB else 0) |
35103511 @as(u32, if (@hasDecl(posix.MSG, "FASTOPEN") and flags.fastopen) posix.MSG.FASTOPEN else 0) |
35113512 posix.MSG.NOSIGNAL;
35123513
......@@ -3522,6 +3523,21 @@ fn netSend(
35223523 return .{ null, i };
35233524}
35243525
3526fn netSendWindows(
3527 userdata: ?*anyopaque,
3528 handle: net.Socket.Handle,
3529 messages: []net.OutgoingMessage,
3530 flags: net.SendFlags,
3531) struct { ?net.Socket.SendError, usize } {
3532 if (!have_networking) return .{ error.NetworkDown, 0 };
3533 const t: *Threaded = @ptrCast(@alignCast(userdata));
3534 _ = t;
3535 _ = handle;
3536 _ = messages;
3537 _ = flags;
3538 @panic("TODO");
3539}
3540
35253541fn netSendOne(
35263542 t: *Threaded,
35273543 handle: net.Socket.Handle,
......@@ -3676,7 +3692,7 @@ fn netSendMany(
36763692 }
36773693}
36783694
3679fn netReceive(
3695fn netReceivePosix(
36803696 userdata: ?*anyopaque,
36813697 handle: net.Socket.Handle,
36823698 message_buffer: []net.IncomingMessage,
......@@ -3805,6 +3821,25 @@ fn netReceive(
38053821 }
38063822}
38073823
3824fn netReceiveWindows(
3825 userdata: ?*anyopaque,
3826 handle: net.Socket.Handle,
3827 message_buffer: []net.IncomingMessage,
3828 data_buffer: []u8,
3829 flags: net.ReceiveFlags,
3830 timeout: Io.Timeout,
3831) struct { ?net.Socket.ReceiveTimeoutError, usize } {
3832 if (!have_networking) return .{ error.NetworkDown, 0 };
3833 const t: *Threaded = @ptrCast(@alignCast(userdata));
3834 _ = t;
3835 _ = handle;
3836 _ = message_buffer;
3837 _ = data_buffer;
3838 _ = flags;
3839 _ = timeout;
3840 @panic("TODO");
3841}
3842
38083843fn netWritePosix(
38093844 userdata: ?*anyopaque,
38103845 fd: net.Socket.Handle,
......@@ -3887,6 +3922,22 @@ fn netWritePosix(
38873922 }
38883923}
38893924
3925fn netWriteWindows(
3926 userdata: ?*anyopaque,
3927 handle: net.Socket.Handle,
3928 header: []const u8,
3929 data: []const []const u8,
3930 splat: usize,
3931) net.Stream.Writer.Error!usize {
3932 const t: *Threaded = @ptrCast(@alignCast(userdata));
3933 _ = t;
3934 _ = handle;
3935 _ = header;
3936 _ = data;
3937 _ = splat;
3938 @panic("TODO");
3939}
3940
38903941fn addBuf(v: []posix.iovec_const, i: *@FieldType(posix.msghdr_const, "iovlen"), bytes: []const u8) void {
38913942 // OS checks ptr addr before length so zero length vectors must be omitted.
38923943 if (bytes.len == 0) return;
......@@ -3899,7 +3950,7 @@ fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void {
38993950 const t: *Threaded = @ptrCast(@alignCast(userdata));
39003951 _ = t;
39013952 switch (native_os) {
3902 .windows => closeSocketWindows(handle) catch recoverableOsBugDetected(),
3953 .windows => closeSocketWindows(handle),
39033954 else => posix.close(handle),
39043955 }
39053956}
......@@ -4559,7 +4610,7 @@ fn lookupDns(
45594610 message_i += 1;
45604611 }
45614612 }
4562 _ = netSend(t, socket.handle, message_buffer[0..message_i], .{});
4613 _ = netSendPosix(t, socket.handle, message_buffer[0..message_i], .{});
45634614 }
45644615
45654616 const timeout: Io.Timeout = .{ .deadline = .{
......@@ -4607,7 +4658,7 @@ fn lookupDns(
46074658 .data_ptr = query.ptr,
46084659 .data_len = query.len,
46094660 };
4610 _ = netSend(t, socket.handle, (&retry_message)[0..1], .{});
4661 _ = netSendPosix(t, socket.handle, (&retry_message)[0..1], .{});
46114662 continue;
46124663 },
46134664 else => continue,
......@@ -5157,9 +5208,9 @@ fn closeSocketWindows(s: ws2_32.SOCKET) void {
51575208 if (builtin.mode == .Debug) switch (rc) {
51585209 0 => {},
51595210 ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
5160 else => unreachable,
5211 else => recoverableOsBugDetected(),
51615212 },
5162 else => unreachable,
5213 else => recoverableOsBugDetected(),
51635214 };
51645215}
51655216
lib/std/Io/net/test.zig-18
......@@ -186,15 +186,6 @@ test "listen on a port, send bytes, receive bytes" {
186186
187187 const io = testing.io;
188188
189 if (builtin.os.tag == .windows) {
190 _ = try std.os.windows.WSAStartup(2, 2);
191 }
192 defer {
193 if (builtin.os.tag == .windows) {
194 std.os.windows.WSACleanup() catch unreachable;
195 }
196 }
197
198189 // Try only the IPv4 variant as some CI builders have no IPv6 localhost
199190 // configured.
200191 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
......@@ -282,15 +273,6 @@ test "listen on a unix socket, send bytes, receive bytes" {
282273
283274 const io = testing.io;
284275
285 if (builtin.os.tag == .windows) {
286 _ = try std.os.windows.WSAStartup(2, 2);
287 }
288 defer {
289 if (builtin.os.tag == .windows) {
290 std.os.windows.WSACleanup() catch unreachable;
291 }
292 }
293
294276 const socket_path = try generateFileName("socket.unix");
295277 defer testing.allocator.free(socket_path);
296278
lib/std/crypto/Certificate/Bundle.zig+4-2
......@@ -144,10 +144,12 @@ fn rescanWithPath(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp, cert_f
144144
145145const RescanWindowsError = Allocator.Error || ParseCertError || std.posix.UnexpectedError || error{FileNotFound};
146146
147fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void {
147fn rescanWindows(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp) RescanWindowsError!void {
148148 cb.bytes.clearRetainingCapacity();
149149 cb.map.clearRetainingCapacity();
150150
151 _ = io;
152
151153 const w = std.os.windows;
152154 const GetLastError = w.GetLastError;
153155 const root = [4:0]u16{ 'R', 'O', 'O', 'T' };
......@@ -157,7 +159,7 @@ fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void {
157159 };
158160 defer _ = w.crypt32.CertCloseStore(store, 0);
159161
160 const now_sec = std.time.timestamp();
162 const now_sec = now.toSeconds();
161163
162164 var ctx = w.crypt32.CertEnumCertificatesInStore(store, null);
163165 while (ctx) |context| : (ctx = w.crypt32.CertEnumCertificatesInStore(store, ctx)) {
lib/std/os/windows.zig+7
......@@ -2735,6 +2735,13 @@ pub fn statusBug(status: NTSTATUS) UnexpectedError {
27352735 }
27362736}
27372737
2738pub fn errorBug(err: Win32Error) UnexpectedError {
2739 switch (builtin.mode) {
2740 .Debug => std.debug.panic("programmer bug caused syscall status: {t}", .{err}),
2741 else => return error.Unexpected,
2742 }
2743}
2744
27382745pub const Win32Error = @import("windows/win32error.zig").Win32Error;
27392746pub const NTSTATUS = @import("windows/ntstatus.zig").NTSTATUS;
27402747pub const LANG = @import("windows/lang.zig");