authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-05 17:46:07-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-08 19:20:06-07:00
logc2ebbd8911d7eec337203bd1de1fd2f3a59273c8
treee7e23fea59493f11bbd829584042beee1498de32
parent8b69341271db18a03a4aa7400a4c6679c5fbf5c6

std.Io.Threaded: implement net_receive for Windows


2 files changed, 131 insertions(+), 25 deletions(-)

lib/std/Io/Threaded.zig+117-14
...@@ -2649,7 +2649,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper...@@ -2649,7 +2649,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
2649fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {2649fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
2650 const t: *Threaded = @ptrCast(@alignCast(userdata));2650 const t: *Threaded = @ptrCast(@alignCast(userdata));
2651 if (is_windows) {2651 if (is_windows) {
2652 batchDrainSubmittedWindows(b, false) catch |err| switch (err) {2652 batchDrainSubmittedWindows(t, b, false) catch |err| switch (err) {
2653 error.ConcurrencyUnavailable => unreachable, // passed concurrency=false2653 error.ConcurrencyUnavailable => unreachable, // passed concurrency=false
2654 else => |e| return e,2654 else => |e| return e,
2655 };2655 };
...@@ -2789,7 +2789,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout...@@ -2789,7 +2789,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
2789 const t: *Threaded = @ptrCast(@alignCast(userdata));2789 const t: *Threaded = @ptrCast(@alignCast(userdata));
2790 if (is_windows) {2790 if (is_windows) {
2791 const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t));2791 const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t));
2792 try batchDrainSubmittedWindows(b, true);2792 try batchDrainSubmittedWindows(t, b, true);
2793 while (b.pending.head != .none and b.completed.head == .none) {2793 while (b.pending.head != .none and b.completed.head == .none) {
2794 var delay_interval: windows.LARGE_INTEGER = interval: {2794 var delay_interval: windows.LARGE_INTEGER = interval: {
2795 const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER);2795 const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER);
...@@ -3005,6 +3005,31 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void {...@@ -3005,6 +3005,31 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void {
3005 }3005 }
3006}3006}
30073007
3008fn batchCompleteBlockingWindows(
3009 b: *Io.Batch,
3010 operation_userdata: *WindowsBatchOperationUserdata,
3011 result: Io.Operation.Result,
3012) void {
3013 const erased_userdata = operation_userdata.toErased();
3014 const pending: *Io.Operation.Storage.Pending = @fieldParentPtr("userdata", erased_userdata);
3015 switch (pending.node.prev) {
3016 .none => b.pending.head = pending.node.next,
3017 else => |prev_index| b.storage[prev_index.toIndex()].pending.node.next = pending.node.next,
3018 }
3019 switch (pending.node.next) {
3020 .none => b.pending.tail = pending.node.prev,
3021 else => |next_index| b.storage[next_index.toIndex()].pending.node.prev = pending.node.prev,
3022 }
3023 const storage: *Io.Operation.Storage = @fieldParentPtr("pending", pending);
3024 const index: Io.Operation.OptionalIndex = .fromIndex(storage - b.storage.ptr);
3025 switch (b.completed.tail) {
3026 .none => b.completed.head = index,
3027 else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
3028 }
3029 b.completed.tail = index;
3030 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
3031}
3032
3008fn batchApc(3033fn batchApc(
3009 apc_context: ?*anyopaque,3034 apc_context: ?*anyopaque,
3010 iosb: *windows.IO_STATUS_BLOCK,3035 iosb: *windows.IO_STATUS_BLOCK,
...@@ -3044,7 +3069,7 @@ fn batchApc(...@@ -3044,7 +3069,7 @@ fn batchApc(
3044 .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) },3069 .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) },
3045 .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) },3070 .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) },
3046 .device_io_control => .{ .device_io_control = iosb.* },3071 .device_io_control => .{ .device_io_control = iosb.* },
3047 .net_receive => unreachable, // TODO3072 .net_receive => unreachable,
3048 };3073 };
3049 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };3074 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
3050 },3075 },
...@@ -3052,7 +3077,7 @@ fn batchApc(...@@ -3052,7 +3077,7 @@ fn batchApc(
3052}3077}
30533078
3054/// If `concurrency` is false, `error.ConcurrencyUnavailable` is unreachable.3079/// If `concurrency` is false, `error.ConcurrencyUnavailable` is unreachable.
3055fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentError || Io.Cancelable)!void {3080fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io.ConcurrentError || Io.Cancelable)!void {
3056 var index = b.submitted.head;3081 var index = b.submitted.head;
3057 errdefer b.submitted.head = index;3082 errdefer b.submitted.head = index;
3058 while (index != .none) {3083 while (index != .none) {
...@@ -3246,10 +3271,12 @@ fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentErr...@@ -3246,10 +3271,12 @@ fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentErr
3246 };3271 };
3247 }3272 }
3248 },3273 },
3249 .net_receive => |o| {3274 .net_receive => |*o| {
3275 // TODO integrate with overlapped I/O or equivalent to avoid this error
3250 if (concurrency) return error.ConcurrencyUnavailable;3276 if (concurrency) return error.ConcurrencyUnavailable;
3251 _ = o;3277 batchCompleteBlockingWindows(b, operation_userdata, .{
3252 @panic("TODO implement Batch NetReceive on Windows");3278 .net_receive = netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags),
3279 });
3253 },3280 },
3254 }3281 }
3255 index = submission.node.next;3282 index = submission.node.next;
...@@ -13323,13 +13350,89 @@ fn netReceiveWindows(...@@ -13323,13 +13350,89 @@ fn netReceiveWindows(
13323 data_buffer: []u8,13350 data_buffer: []u8,
13324 flags: net.ReceiveFlags,13351 flags: net.ReceiveFlags,
13325) struct { ?net.Socket.ReceiveError, usize } {13352) struct { ?net.Socket.ReceiveError, usize } {
13326 if (!have_networking) return .{ error.NetworkDown, 0 };13353 netReceiveWindowsOne(t, socket_handle, &message_buffer[0], data_buffer, flags) catch |err| return .{ err, 0 };
13327 _ = t;13354 return .{ null, 1 };
13328 _ = socket_handle;13355}
13329 _ = message_buffer;13356
13330 _ = data_buffer;13357fn netReceiveWindowsOne(
13331 _ = flags;13358 t: *Threaded,
13332 @panic("TODO implement netReceiveWindows");13359 socket_handle: net.Socket.Handle,
13360 message: *net.IncomingMessage,
13361 data_buffer: []u8,
13362 flags: net.ReceiveFlags,
13363) net.Socket.ReceiveError!void {
13364 comptime assert(have_networking);
13365
13366 var windows_flags: u32 =
13367 @as(u32, if (flags.oob) ws2_32.MSG.OOB else 0) |
13368 @as(u32, if (flags.peek) ws2_32.MSG.PEEK else 0) |
13369 @as(u32, if (flags.trunc) ws2_32.MSG.TRUNC else 0);
13370
13371 var buf: ws2_32.WSABUF = .{
13372 .buf = data_buffer.ptr,
13373 .len = std.math.cast(u32, data_buffer.len) orelse return error.MessageOversize,
13374 };
13375 var n: u32 = undefined;
13376 var syscall: Syscall = try .start();
13377 var from_storage: WsaAddress = undefined;
13378 var from_storage_len: i32 = @sizeOf(WsaAddress);
13379
13380 while (true) {
13381 const rc = ws2_32.WSARecvFrom(
13382 socket_handle,
13383 (&buf)[0..1],
13384 1,
13385 &n,
13386 &windows_flags,
13387 &from_storage.any,
13388 &from_storage_len,
13389 null,
13390 null,
13391 );
13392 if (rc != ws2_32.SOCKET_ERROR) {
13393 syscall.finish();
13394 message.* = .{
13395 .from = addressFromWsa(&from_storage),
13396 .data = data_buffer[0..n],
13397 .control = &.{},
13398 .flags = .{
13399 .eor = false,
13400 .trunc = (windows_flags & ws2_32.MSG.TRUNC) != 0,
13401 .ctrunc = (windows_flags & ws2_32.MSG.CTRUNC) != 0,
13402 .oob = false,
13403 .errqueue = false,
13404 },
13405 };
13406 return;
13407 }
13408 switch (ws2_32.WSAGetLastError()) {
13409 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
13410 try syscall.checkCancel();
13411 continue;
13412 },
13413 .NOTINITIALISED => {
13414 syscall.finish();
13415 try initializeWsa(t);
13416 syscall = try .start();
13417 continue;
13418 },
13419
13420 .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
13421 .ENETDOWN => return syscall.fail(error.NetworkDown),
13422 .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
13423 .ENOTCONN => return syscall.fail(error.SocketUnconnected),
13424 .EFAULT => unreachable, // a pointer is not completely contained in user address space.
13425
13426 else => |err| {
13427 syscall.finish();
13428 switch (err) {
13429 .EINVAL => return wsaErrorBug(err),
13430 .EMSGSIZE => return wsaErrorBug(err),
13431 else => return windows.unexpectedWSAError(err),
13432 }
13433 },
13434 }
13435 }
13333}13436}
1333413437
13335fn netReceiveUnavailable(13438fn netReceiveUnavailable(
lib/std/os/windows/ws2_32.zig+14-11
...@@ -661,17 +661,20 @@ pub const IOC_OUT = 1073741824;...@@ -661,17 +661,20 @@ pub const IOC_OUT = 1073741824;
661pub const IOC_IN = 2147483648;661pub const IOC_IN = 2147483648;
662662
663pub const MSG = struct {663pub const MSG = struct {
664 pub const TRUNC = 256;664 pub const OOB = 0x1;
665 pub const CTRUNC = 512;665 pub const PEEK = 0x2;
666 pub const BCAST = 1024;666 pub const DONTROUTE = 0x4;
667 pub const MCAST = 2048;667 pub const WAITALL = 0x8;
668 pub const ERRQUEUE = 4096;668 pub const INTERRUPT = 0x10;
669669 pub const PUSH_IMMEDIATE = 0x20;
670 pub const PEEK = 2;670
671 pub const WAITALL = 8;671 pub const TRUNC = 0x0100;
672 pub const PUSH_IMMEDIATE = 32;672 pub const CTRUNC = 0x0200;
673 pub const PARTIAL = 32768;673 pub const BCAST = 0x0400;
674 pub const INTERRUPT = 16;674 pub const MCAST = 0x0800;
675
676 pub const PARTIAL = 0x8000;
677
675 pub const MAXIOVLEN = 16;678 pub const MAXIOVLEN = 16;
676};679};
677680