| ... | @@ -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 |
| 2649 | fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { | 2649 | fn 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=false | 2653 | 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 | } |
| 3007 | | 3007 | |
| | 3008 | fn 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 | |
| 3008 | fn batchApc( | 3033 | fn 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, // TODO | 3072 | .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 | } |
| 3053 | | 3078 | |
| 3054 | /// If `concurrency` is false, `error.ConcurrencyUnavailable` is unreachable. | 3079 | /// If `concurrency` is false, `error.ConcurrencyUnavailable` is unreachable. |
| 3055 | fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentError || Io.Cancelable)!void { | 3080 | fn 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; | 13357 | fn 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 | } |
| 13334 | | 13437 | |
| 13335 | fn netReceiveUnavailable( | 13438 | fn netReceiveUnavailable( |