| ... | ... | @@ -61,7 +61,7 @@ disable_memory_mapping: bool, |
| 61 | 61 | |
| 62 | 62 | stderr_writer: File.Writer = .{ |
| 63 | 63 | .io = undefined, |
| 64 | | .interface = Io.File.Writer.initInterface(&.{}), |
| 64 | .interface = File.Writer.initInterface(&.{}), |
| 65 | 65 | .file = if (is_windows) undefined else .stderr(), |
| 66 | 66 | .mode = .streaming, |
| 67 | 67 | }, |
| ... | ... | @@ -160,7 +160,7 @@ pub const Environ = struct { |
| 160 | 160 | }, |
| 161 | 161 | }; |
| 162 | 162 | |
| 163 | | pub fn scan(environ: *Environ, allocator: std.mem.Allocator) void { |
| 163 | pub fn scan(environ: *Environ, allocator: Allocator) void { |
| 164 | 164 | if (is_windows) { |
| 165 | 165 | // This value expires with any call that modifies the environment, |
| 166 | 166 | // which is outside of this Io implementation's control, so references |
| ... | ... | @@ -1901,10 +1901,6 @@ pub fn io(t: *Threaded) Io { |
| 1901 | 1901 | .windows => netSendWindows, |
| 1902 | 1902 | else => netSendPosix, |
| 1903 | 1903 | }, |
| 1904 | | .netReceive = switch (native_os) { |
| 1905 | | .windows => netReceiveWindows, |
| 1906 | | else => netReceivePosix, |
| 1907 | | }, |
| 1908 | 1904 | .netInterfaceNameResolve = netInterfaceNameResolve, |
| 1909 | 1905 | .netInterfaceName = netInterfaceName, |
| 1910 | 1906 | .netLookup = netLookup, |
| ... | ... | @@ -2037,7 +2033,6 @@ pub fn ioBasic(t: *Threaded) Io { |
| 2037 | 2033 | .netWrite = netWriteUnavailable, |
| 2038 | 2034 | .netWriteFile = netWriteFileUnavailable, |
| 2039 | 2035 | .netSend = netSendUnavailable, |
| 2040 | | .netReceive = netReceiveUnavailable, |
| 2041 | 2036 | .netInterfaceNameResolve = netInterfaceNameResolveUnavailable, |
| 2042 | 2037 | .netInterfaceName = netInterfaceNameUnavailable, |
| 2043 | 2038 | .netLookup = netLookupUnavailable, |
| ... | ... | @@ -2638,6 +2633,15 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper |
| 2638 | 2633 | }, |
| 2639 | 2634 | }, |
| 2640 | 2635 | .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) }, |
| 2636 | .net_receive => |*o| return .{ .net_receive = o: { |
| 2637 | if (!have_networking) break :o .{ error.NetworkDown, 0 }; |
| 2638 | if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags); |
| 2639 | netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags) catch |err| switch (err) { |
| 2640 | error.Canceled => |e| return e, |
| 2641 | else => |e| break :o .{ e, 0 }, |
| 2642 | }; |
| 2643 | break :o .{ null, 1 }; |
| 2644 | } }, |
| 2641 | 2645 | } |
| 2642 | 2646 | } |
| 2643 | 2647 | |
| ... | ... | @@ -2662,11 +2666,19 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { |
| 2662 | 2666 | const submission = &b.storage[index.toIndex()].submission; |
| 2663 | 2667 | switch (submission.operation) { |
| 2664 | 2668 | .file_read_streaming => |o| { |
| 2665 | | poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.IN, .revents = 0 }; |
| 2669 | poll_buffer[poll_len] = .{ |
| 2670 | .fd = o.file.handle, |
| 2671 | .events = posix.POLL.IN | posix.POLL.ERR, |
| 2672 | .revents = 0, |
| 2673 | }; |
| 2666 | 2674 | poll_len += 1; |
| 2667 | 2675 | }, |
| 2668 | 2676 | .file_write_streaming => |o| { |
| 2669 | | poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.OUT, .revents = 0 }; |
| 2677 | poll_buffer[poll_len] = .{ |
| 2678 | .fd = o.file.handle, |
| 2679 | .events = posix.POLL.OUT | posix.POLL.ERR, |
| 2680 | .revents = 0, |
| 2681 | }; |
| 2670 | 2682 | poll_len += 1; |
| 2671 | 2683 | }, |
| 2672 | 2684 | .device_io_control => |o| { |
| ... | ... | @@ -2677,6 +2689,14 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { |
| 2677 | 2689 | }; |
| 2678 | 2690 | poll_len += 1; |
| 2679 | 2691 | }, |
| 2692 | .net_receive => |*o| { |
| 2693 | poll_buffer[poll_len] = .{ |
| 2694 | .fd = o.socket_handle, |
| 2695 | .events = posix.POLL.IN | posix.POLL.ERR, |
| 2696 | .revents = 0, |
| 2697 | }; |
| 2698 | poll_len += 1; |
| 2699 | }, |
| 2680 | 2700 | } |
| 2681 | 2701 | index = submission.node.next; |
| 2682 | 2702 | } |
| ... | ... | @@ -2796,12 +2816,12 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2796 | 2816 | if (!have_poll) return error.ConcurrencyUnavailable; |
| 2797 | 2817 | var poll_buffer: [poll_buffer_len]posix.pollfd = undefined; |
| 2798 | 2818 | var poll_storage: struct { |
| 2799 | | gpa: std.mem.Allocator, |
| 2819 | gpa: Allocator, |
| 2800 | 2820 | batch: *Io.Batch, |
| 2801 | 2821 | slice: []posix.pollfd, |
| 2802 | 2822 | len: u32, |
| 2803 | 2823 | |
| 2804 | | fn add(storage: *@This(), file: Io.File, events: @FieldType(posix.pollfd, "events")) Io.ConcurrentError!void { |
| 2824 | fn add(storage: *@This(), fd: File.Handle, events: @FieldType(posix.pollfd, "events")) Io.ConcurrentError!void { |
| 2805 | 2825 | const len = storage.len; |
| 2806 | 2826 | if (len == poll_buffer_len) { |
| 2807 | 2827 | const slice: []posix.pollfd = if (storage.batch.userdata) |batch_userdata| |
| ... | ... | @@ -2816,7 +2836,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2816 | 2836 | storage.slice = slice; |
| 2817 | 2837 | } |
| 2818 | 2838 | storage.slice[len] = .{ |
| 2819 | | .fd = file.handle, |
| 2839 | .fd = fd, |
| 2820 | 2840 | .events = events, |
| 2821 | 2841 | .revents = 0, |
| 2822 | 2842 | }; |
| ... | ... | @@ -2828,9 +2848,10 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2828 | 2848 | while (index != .none) { |
| 2829 | 2849 | const submission = &b.storage[index.toIndex()].submission; |
| 2830 | 2850 | switch (submission.operation) { |
| 2831 | | .file_read_streaming => |o| try poll_storage.add(o.file, posix.POLL.IN), |
| 2832 | | .file_write_streaming => |o| try poll_storage.add(o.file, posix.POLL.OUT), |
| 2833 | | .device_io_control => |o| try poll_storage.add(o.file, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR), |
| 2851 | .file_read_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.ERR), |
| 2852 | .file_write_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.OUT | posix.POLL.ERR), |
| 2853 | .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR), |
| 2854 | .net_receive => |o| try poll_storage.add(o.socket_handle, posix.POLL.IN | posix.POLL.ERR), |
| 2834 | 2855 | } |
| 2835 | 2856 | index = submission.node.next; |
| 2836 | 2857 | } |
| ... | ... | @@ -3000,6 +3021,7 @@ fn batchApc( |
| 3000 | 3021 | .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) }, |
| 3001 | 3022 | .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) }, |
| 3002 | 3023 | .device_io_control => .{ .device_io_control = iosb.* }, |
| 3024 | .net_receive => unreachable, // TODO |
| 3003 | 3025 | }; |
| 3004 | 3026 | storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; |
| 3005 | 3027 | }, |
| ... | ... | @@ -3201,6 +3223,11 @@ fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentErr |
| 3201 | 3223 | }; |
| 3202 | 3224 | } |
| 3203 | 3225 | }, |
| 3226 | .net_receive => |o| { |
| 3227 | if (concurrency) return error.ConcurrencyUnavailable; |
| 3228 | _ = o; |
| 3229 | @panic("TODO implement Batch NetReceive on Windows"); |
| 3230 | }, |
| 3204 | 3231 | } |
| 3205 | 3232 | index = submission.node.next; |
| 3206 | 3233 | } |
| ... | ... | @@ -13190,70 +13217,42 @@ fn netSendMany( |
| 13190 | 13217 | } |
| 13191 | 13218 | |
| 13192 | 13219 | fn netReceivePosix( |
| 13193 | | userdata: ?*anyopaque, |
| 13194 | | handle: net.Socket.Handle, |
| 13195 | | message_buffer: []net.IncomingMessage, |
| 13220 | socket_handle: net.Socket.Handle, |
| 13221 | message: *net.IncomingMessage, |
| 13196 | 13222 | data_buffer: []u8, |
| 13197 | 13223 | flags: net.ReceiveFlags, |
| 13198 | | timeout: Io.Timeout, |
| 13199 | | ) struct { ?net.Socket.ReceiveTimeoutError, usize } { |
| 13200 | | if (!have_networking) return .{ error.NetworkDown, 0 }; |
| 13201 | | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 13202 | | const t_io = io(t); |
| 13203 | | |
| 13224 | ) net.Socket.ReceiveError!void { |
| 13204 | 13225 | // recvmmsg is useless, here's why: |
| 13205 | 13226 | // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371) |
| 13206 | 13227 | // * it wants iovecs for each message but we have a better API: one data |
| 13207 | 13228 | // buffer to handle all the messages. The better API cannot be lowered to |
| 13208 | 13229 | // the split vectors though because reducing the buffer size might make |
| 13209 | 13230 | // some messages unreceivable. |
| 13210 | | |
| 13211 | | // So the strategy instead is to use non-blocking recvmsg calls, calling |
| 13212 | | // poll() with timeout if the first one returns EAGAIN. |
| 13213 | 13231 | const posix_flags: u32 = |
| 13214 | 13232 | @as(u32, if (flags.oob) posix.MSG.OOB else 0) | |
| 13215 | 13233 | @as(u32, if (flags.peek) posix.MSG.PEEK else 0) | |
| 13216 | 13234 | @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) | |
| 13217 | | posix.MSG.DONTWAIT | posix.MSG.NOSIGNAL; |
| 13235 | posix.MSG.NOSIGNAL; |
| 13218 | 13236 | |
| 13219 | | var poll_fds: [1]posix.pollfd = .{ |
| 13220 | | .{ |
| 13221 | | .fd = handle, |
| 13222 | | .events = posix.POLL.IN, |
| 13223 | | .revents = undefined, |
| 13224 | | }, |
| 13237 | var storage: PosixAddress = undefined; |
| 13238 | var iov: posix.iovec = .{ .base = data_buffer.ptr, .len = data_buffer.len }; |
| 13239 | var msg: posix.msghdr = .{ |
| 13240 | .name = &storage.any, |
| 13241 | .namelen = @sizeOf(PosixAddress), |
| 13242 | .iov = (&iov)[0..1], |
| 13243 | .iovlen = 1, |
| 13244 | .control = message.control.ptr, |
| 13245 | .controllen = @intCast(message.control.len), |
| 13246 | .flags = undefined, |
| 13225 | 13247 | }; |
| 13226 | | var message_i: usize = 0; |
| 13227 | | var data_i: usize = 0; |
| 13228 | | |
| 13229 | | const deadline = timeout.toTimestamp(t_io); |
| 13230 | 13248 | |
| 13231 | | recv: while (true) { |
| 13232 | | if (message_buffer.len - message_i == 0) return .{ null, message_i }; |
| 13233 | | const message = &message_buffer[message_i]; |
| 13234 | | const remaining_data_buffer = data_buffer[data_i..]; |
| 13235 | | var storage: PosixAddress = undefined; |
| 13236 | | var iov: posix.iovec = .{ .base = remaining_data_buffer.ptr, .len = remaining_data_buffer.len }; |
| 13237 | | var msg: posix.msghdr = .{ |
| 13238 | | .name = &storage.any, |
| 13239 | | .namelen = @sizeOf(PosixAddress), |
| 13240 | | .iov = (&iov)[0..1], |
| 13241 | | .iovlen = 1, |
| 13242 | | .control = message.control.ptr, |
| 13243 | | .controllen = @intCast(message.control.len), |
| 13244 | | .flags = undefined, |
| 13245 | | }; |
| 13246 | | |
| 13247 | | const recv_rc = rc: { |
| 13248 | | const syscall = Syscall.start() catch |err| return .{ err, message_i }; |
| 13249 | | const rc = posix.system.recvmsg(handle, &msg, posix_flags); |
| 13250 | | syscall.finish(); |
| 13251 | | break :rc rc; |
| 13252 | | }; |
| 13253 | | switch (posix.errno(recv_rc)) { |
| 13249 | const syscall = try Syscall.start(); |
| 13250 | while (true) { |
| 13251 | const rc = posix.system.recvmsg(socket_handle, &msg, posix_flags); |
| 13252 | switch (posix.errno(rc)) { |
| 13254 | 13253 | .SUCCESS => { |
| 13255 | | const data = remaining_data_buffer[0..@intCast(recv_rc)]; |
| 13256 | | data_i += data.len; |
| 13254 | syscall.finish(); |
| 13255 | const data = data_buffer[0..@intCast(rc)]; |
| 13257 | 13256 | message.* = .{ |
| 13258 | 13257 | .from = addressFromPosix(&storage), |
| 13259 | 13258 | .data = data, |
| ... | ... | @@ -13266,78 +13265,45 @@ fn netReceivePosix( |
| 13266 | 13265 | .errqueue = if (@hasDecl(posix.MSG, "ERRQUEUE")) (msg.flags & posix.MSG.ERRQUEUE) != 0 else false, |
| 13267 | 13266 | }, |
| 13268 | 13267 | }; |
| 13269 | | message_i += 1; |
| 13270 | | continue; |
| 13268 | return; |
| 13271 | 13269 | }, |
| 13272 | | .AGAIN => while (true) { |
| 13273 | | if (message_i != 0) return .{ null, message_i }; |
| 13274 | | |
| 13275 | | const max_poll_ms = std.math.maxInt(u31); |
| 13276 | | const timeout_ms: u31 = if (deadline) |d| t: { |
| 13277 | | const duration = d.durationFromNow(t_io); |
| 13278 | | if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i }; |
| 13279 | | break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds())); |
| 13280 | | } else max_poll_ms; |
| 13281 | | |
| 13282 | | const syscall = Syscall.start() catch |err| return .{ err, message_i }; |
| 13283 | | const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms); |
| 13284 | | syscall.finish(); |
| 13285 | | |
| 13286 | | switch (posix.errno(poll_rc)) { |
| 13287 | | .SUCCESS => { |
| 13288 | | if (poll_rc == 0) { |
| 13289 | | // Although spurious timeouts are OK, when no deadline |
| 13290 | | // is passed we must not return `error.Timeout`. |
| 13291 | | if (deadline == null) continue; |
| 13292 | | return .{ error.Timeout, message_i }; |
| 13293 | | } |
| 13294 | | continue :recv; |
| 13295 | | }, |
| 13296 | | .INTR => continue, |
| 13297 | | |
| 13298 | | .FAULT => |err| return .{ errnoBug(err), message_i }, |
| 13299 | | .INVAL => |err| return .{ errnoBug(err), message_i }, |
| 13300 | | .NOMEM => return .{ error.SystemResources, message_i }, |
| 13301 | | else => |err| return .{ posix.unexpectedErrno(err), message_i }, |
| 13302 | | } |
| 13270 | .INTR => { |
| 13271 | try syscall.checkCancel(); |
| 13272 | continue; |
| 13303 | 13273 | }, |
| 13304 | | .INTR => continue, |
| 13305 | | |
| 13306 | | .BADF => |err| return .{ errnoBug(err), message_i }, |
| 13307 | | .NFILE => return .{ error.SystemFdQuotaExceeded, message_i }, |
| 13308 | | .MFILE => return .{ error.ProcessFdQuotaExceeded, message_i }, |
| 13309 | | .FAULT => |err| return .{ errnoBug(err), message_i }, |
| 13310 | | .INVAL => |err| return .{ errnoBug(err), message_i }, |
| 13311 | | .NOBUFS => return .{ error.SystemResources, message_i }, |
| 13312 | | .NOMEM => return .{ error.SystemResources, message_i }, |
| 13313 | | .NOTCONN => return .{ error.SocketUnconnected, message_i }, |
| 13314 | | .NOTSOCK => |err| return .{ errnoBug(err), message_i }, |
| 13315 | | .MSGSIZE => return .{ error.MessageOversize, message_i }, |
| 13316 | | .PIPE => return .{ error.SocketUnconnected, message_i }, |
| 13317 | | .OPNOTSUPP => |err| return .{ errnoBug(err), message_i }, |
| 13318 | | .CONNRESET => return .{ error.ConnectionResetByPeer, message_i }, |
| 13319 | | .NETDOWN => return .{ error.NetworkDown, message_i }, |
| 13320 | | else => |err| return .{ posix.unexpectedErrno(err), message_i }, |
| 13274 | .NFILE => return syscall.fail(error.SystemFdQuotaExceeded), |
| 13275 | .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded), |
| 13276 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 13277 | .NOMEM => return syscall.fail(error.SystemResources), |
| 13278 | .NOTCONN => return syscall.fail(error.SocketUnconnected), |
| 13279 | .MSGSIZE => return syscall.fail(error.MessageOversize), |
| 13280 | .PIPE => return syscall.fail(error.SocketUnconnected), |
| 13281 | .CONNRESET => return syscall.fail(error.ConnectionResetByPeer), |
| 13282 | .NETDOWN => return syscall.fail(error.NetworkDown), |
| 13283 | .AGAIN => |err| return syscall.errnoBug(err), |
| 13284 | .BADF => |err| return syscall.errnoBug(err), |
| 13285 | .FAULT => |err| return syscall.errnoBug(err), |
| 13286 | .INVAL => |err| return syscall.errnoBug(err), |
| 13287 | .NOTSOCK => |err| return syscall.errnoBug(err), |
| 13288 | .OPNOTSUPP => |err| return syscall.errnoBug(err), |
| 13289 | else => |err| return syscall.unexpectedErrno(err), |
| 13321 | 13290 | } |
| 13322 | 13291 | } |
| 13323 | 13292 | } |
| 13324 | 13293 | |
| 13325 | 13294 | fn netReceiveWindows( |
| 13326 | | userdata: ?*anyopaque, |
| 13327 | | handle: net.Socket.Handle, |
| 13295 | t: *Threaded, |
| 13296 | socket_handle: net.Socket.Handle, |
| 13328 | 13297 | message_buffer: []net.IncomingMessage, |
| 13329 | 13298 | data_buffer: []u8, |
| 13330 | 13299 | flags: net.ReceiveFlags, |
| 13331 | | timeout: Io.Timeout, |
| 13332 | | ) struct { ?net.Socket.ReceiveTimeoutError, usize } { |
| 13300 | ) struct { ?net.Socket.ReceiveError, usize } { |
| 13333 | 13301 | if (!have_networking) return .{ error.NetworkDown, 0 }; |
| 13334 | | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 13335 | 13302 | _ = t; |
| 13336 | | _ = handle; |
| 13303 | _ = socket_handle; |
| 13337 | 13304 | _ = message_buffer; |
| 13338 | 13305 | _ = data_buffer; |
| 13339 | 13306 | _ = flags; |
| 13340 | | _ = timeout; |
| 13341 | 13307 | @panic("TODO implement netReceiveWindows"); |
| 13342 | 13308 | } |
| 13343 | 13309 | |