From be0f188990d8aedc3f5bea5c9ab436a7f778d59d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Feb 2026 00:05:51 -0800 Subject: [PATCH 1/7] std.Io: move netReceive to become an Operation Notably, the timeout becomes provided by the general-purpose Batch API rather than being special-purposed. --- lib/std/Io.zig | 46 ++++++++- lib/std/Io/Threaded.zig | 206 +++++++++++++++++----------------------- lib/std/Io/net.zig | 37 +++----- 3 files changed, 145 insertions(+), 144 deletions(-) diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 932879c99e82b8e7a375148d92a99d05818ea641..87f342f640316343440e5f93e2c1924b81a51c6e 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -243,7 +243,6 @@ pub const VTable = struct { netConnectUnix: *const fn (?*anyopaque, *const net.UnixAddress) net.UnixAddress.ConnectError!net.Socket.Handle, netSocketCreatePair: *const fn (?*anyopaque, net.Socket.CreatePairOptions) net.Socket.CreatePairError![2]net.Socket, netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) struct { ?net.Socket.SendError, usize }, - netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize }, /// Returns 0 on end of stream. netRead: *const fn (?*anyopaque, src: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize, netWrite: *const fn (?*anyopaque, dest: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize, @@ -261,6 +260,7 @@ pub const Operation = union(enum) { /// On Windows this is NtDeviceIoControlFile. On POSIX this is ioctl. On /// other systems this tag is unreachable. device_io_control: DeviceIoControl, + net_receive: NetReceive, pub const Tag = @typeInfo(Operation).@"union".tag_type.?; @@ -350,6 +350,35 @@ pub const Operation = union(enum) { }, }; + pub const NetReceive = struct { + socket_handle: net.Socket.Handle, + message_buffer: []net.IncomingMessage, + data_buffer: []u8, + flags: net.ReceiveFlags, + + pub const Error = error{ + /// Insufficient memory or other resource internal to the operating system. + SystemResources, + /// Per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + /// System-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + /// Local end has been shut down on a connection-oriented socket, or + /// the socket was never connected. + SocketUnconnected, + /// The socket type requires that message be sent atomically, and the + /// size of the message to be sent made this impossible. The message + /// was not transmitted, or was partially transmitted. + MessageOversize, + /// Network connection was unexpectedly closed by sender. + ConnectionResetByPeer, + /// The local network interface used to reach the destination is offline. + NetworkDown, + } || Io.UnexpectedError; + + pub const Result = struct { ?net.Socket.ReceiveError, usize }; + }; + pub const Result = Result: { const operation_fields = @typeInfo(Operation).@"union".fields; var field_names: [operation_fields.len][]const u8 = undefined; @@ -417,6 +446,19 @@ pub fn operate(io: Io, operation: Operation) Cancelable!Operation.Result { return io.vtable.operate(io.userdata, operation); } +pub const OperateTimeoutError = Cancelable || Timeout.Error || ConcurrentError; + +/// Performs one `Operation` with provided `timeout`. +pub fn operateTimeout(io: Io, operation: Operation, timeout: Timeout) OperateTimeoutError!Operation.Result { + var storage: [1]Operation.Storage = undefined; + var batch: Batch = .init(&storage); + batch.addAt(0, operation); + try batch.awaitConcurrent(io, timeout); + const completion = batch.next().?; + assert(completion.index == 0); + return completion.result; +} + /// Submits many operations together without waiting for all of them to /// complete. /// @@ -1716,7 +1758,7 @@ pub const Event = enum(u32) { } /// Blocks until the logical boolean is `true`. - pub fn wait(event: *Event, io: Io) Io.Cancelable!void { + pub fn wait(event: *Event, io: Io) Cancelable!void { if (@cmpxchgStrong(Event, event, .unset, .waiting, .acquire, .acquire)) |prev| switch (prev) { .unset => unreachable, .waiting => {}, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index ca658f0569d7818949293c4b54748865eeef5e02..07d963b292ce0e2d449b212b61a130b75ec56901 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -61,7 +61,7 @@ disable_memory_mapping: bool, stderr_writer: File.Writer = .{ .io = undefined, - .interface = Io.File.Writer.initInterface(&.{}), + .interface = File.Writer.initInterface(&.{}), .file = if (is_windows) undefined else .stderr(), .mode = .streaming, }, @@ -160,7 +160,7 @@ pub const Environ = struct { }, }; - pub fn scan(environ: *Environ, allocator: std.mem.Allocator) void { + pub fn scan(environ: *Environ, allocator: Allocator) void { if (is_windows) { // This value expires with any call that modifies the environment, // which is outside of this Io implementation's control, so references @@ -1901,10 +1901,6 @@ pub fn io(t: *Threaded) Io { .windows => netSendWindows, else => netSendPosix, }, - .netReceive = switch (native_os) { - .windows => netReceiveWindows, - else => netReceivePosix, - }, .netInterfaceNameResolve = netInterfaceNameResolve, .netInterfaceName = netInterfaceName, .netLookup = netLookup, @@ -2037,7 +2033,6 @@ pub fn ioBasic(t: *Threaded) Io { .netWrite = netWriteUnavailable, .netWriteFile = netWriteFileUnavailable, .netSend = netSendUnavailable, - .netReceive = netReceiveUnavailable, .netInterfaceNameResolve = netInterfaceNameResolveUnavailable, .netInterfaceName = netInterfaceNameUnavailable, .netLookup = netLookupUnavailable, @@ -2638,6 +2633,15 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper }, }, .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) }, + .net_receive => |*o| return .{ .net_receive = o: { + if (!have_networking) break :o .{ error.NetworkDown, 0 }; + if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags); + netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| break :o .{ e, 0 }, + }; + break :o .{ null, 1 }; + } }, } } @@ -2662,11 +2666,19 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { const submission = &b.storage[index.toIndex()].submission; switch (submission.operation) { .file_read_streaming => |o| { - poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.IN, .revents = 0 }; + poll_buffer[poll_len] = .{ + .fd = o.file.handle, + .events = posix.POLL.IN | posix.POLL.ERR, + .revents = 0, + }; poll_len += 1; }, .file_write_streaming => |o| { - poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.OUT, .revents = 0 }; + poll_buffer[poll_len] = .{ + .fd = o.file.handle, + .events = posix.POLL.OUT | posix.POLL.ERR, + .revents = 0, + }; poll_len += 1; }, .device_io_control => |o| { @@ -2677,6 +2689,14 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { }; poll_len += 1; }, + .net_receive => |*o| { + poll_buffer[poll_len] = .{ + .fd = o.socket_handle, + .events = posix.POLL.IN | posix.POLL.ERR, + .revents = 0, + }; + poll_len += 1; + }, } index = submission.node.next; } @@ -2796,12 +2816,12 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout if (!have_poll) return error.ConcurrencyUnavailable; var poll_buffer: [poll_buffer_len]posix.pollfd = undefined; var poll_storage: struct { - gpa: std.mem.Allocator, + gpa: Allocator, batch: *Io.Batch, slice: []posix.pollfd, len: u32, - fn add(storage: *@This(), file: Io.File, events: @FieldType(posix.pollfd, "events")) Io.ConcurrentError!void { + fn add(storage: *@This(), fd: File.Handle, events: @FieldType(posix.pollfd, "events")) Io.ConcurrentError!void { const len = storage.len; if (len == poll_buffer_len) { const slice: []posix.pollfd = if (storage.batch.userdata) |batch_userdata| @@ -2816,7 +2836,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout storage.slice = slice; } storage.slice[len] = .{ - .fd = file.handle, + .fd = fd, .events = events, .revents = 0, }; @@ -2828,9 +2848,10 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout while (index != .none) { const submission = &b.storage[index.toIndex()].submission; switch (submission.operation) { - .file_read_streaming => |o| try poll_storage.add(o.file, posix.POLL.IN), - .file_write_streaming => |o| try poll_storage.add(o.file, posix.POLL.OUT), - .device_io_control => |o| try poll_storage.add(o.file, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR), + .file_read_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.ERR), + .file_write_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.OUT | posix.POLL.ERR), + .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR), + .net_receive => |o| try poll_storage.add(o.socket_handle, posix.POLL.IN | posix.POLL.ERR), } index = submission.node.next; } @@ -3000,6 +3021,7 @@ fn batchApc( .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) }, .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) }, .device_io_control => .{ .device_io_control = iosb.* }, + .net_receive => unreachable, // TODO }; storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; }, @@ -3201,6 +3223,11 @@ fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentErr }; } }, + .net_receive => |o| { + if (concurrency) return error.ConcurrencyUnavailable; + _ = o; + @panic("TODO implement Batch NetReceive on Windows"); + }, } index = submission.node.next; } @@ -13190,70 +13217,42 @@ fn netSendMany( } fn netReceivePosix( - userdata: ?*anyopaque, - handle: net.Socket.Handle, - message_buffer: []net.IncomingMessage, + socket_handle: net.Socket.Handle, + message: *net.IncomingMessage, data_buffer: []u8, flags: net.ReceiveFlags, - timeout: Io.Timeout, -) struct { ?net.Socket.ReceiveTimeoutError, usize } { - if (!have_networking) return .{ error.NetworkDown, 0 }; - const t: *Threaded = @ptrCast(@alignCast(userdata)); - const t_io = io(t); - +) net.Socket.ReceiveError!void { // recvmmsg is useless, here's why: // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371) // * it wants iovecs for each message but we have a better API: one data // buffer to handle all the messages. The better API cannot be lowered to // the split vectors though because reducing the buffer size might make // some messages unreceivable. - - // So the strategy instead is to use non-blocking recvmsg calls, calling - // poll() with timeout if the first one returns EAGAIN. const posix_flags: u32 = @as(u32, if (flags.oob) posix.MSG.OOB else 0) | @as(u32, if (flags.peek) posix.MSG.PEEK else 0) | @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) | - posix.MSG.DONTWAIT | posix.MSG.NOSIGNAL; + posix.MSG.NOSIGNAL; - var poll_fds: [1]posix.pollfd = .{ - .{ - .fd = handle, - .events = posix.POLL.IN, - .revents = undefined, - }, + var storage: PosixAddress = undefined; + var iov: posix.iovec = .{ .base = data_buffer.ptr, .len = data_buffer.len }; + var msg: posix.msghdr = .{ + .name = &storage.any, + .namelen = @sizeOf(PosixAddress), + .iov = (&iov)[0..1], + .iovlen = 1, + .control = message.control.ptr, + .controllen = @intCast(message.control.len), + .flags = undefined, }; - var message_i: usize = 0; - var data_i: usize = 0; - const deadline = timeout.toTimestamp(t_io); - - recv: while (true) { - if (message_buffer.len - message_i == 0) return .{ null, message_i }; - const message = &message_buffer[message_i]; - const remaining_data_buffer = data_buffer[data_i..]; - var storage: PosixAddress = undefined; - var iov: posix.iovec = .{ .base = remaining_data_buffer.ptr, .len = remaining_data_buffer.len }; - var msg: posix.msghdr = .{ - .name = &storage.any, - .namelen = @sizeOf(PosixAddress), - .iov = (&iov)[0..1], - .iovlen = 1, - .control = message.control.ptr, - .controllen = @intCast(message.control.len), - .flags = undefined, - }; - - const recv_rc = rc: { - const syscall = Syscall.start() catch |err| return .{ err, message_i }; - const rc = posix.system.recvmsg(handle, &msg, posix_flags); - syscall.finish(); - break :rc rc; - }; - switch (posix.errno(recv_rc)) { + const syscall = try Syscall.start(); + while (true) { + const rc = posix.system.recvmsg(socket_handle, &msg, posix_flags); + switch (posix.errno(rc)) { .SUCCESS => { - const data = remaining_data_buffer[0..@intCast(recv_rc)]; - data_i += data.len; + syscall.finish(); + const data = data_buffer[0..@intCast(rc)]; message.* = .{ .from = addressFromPosix(&storage), .data = data, @@ -13266,78 +13265,45 @@ fn netReceivePosix( .errqueue = if (@hasDecl(posix.MSG, "ERRQUEUE")) (msg.flags & posix.MSG.ERRQUEUE) != 0 else false, }, }; - message_i += 1; + return; + }, + .INTR => { + try syscall.checkCancel(); continue; }, - .AGAIN => while (true) { - if (message_i != 0) return .{ null, message_i }; - - const max_poll_ms = std.math.maxInt(u31); - const timeout_ms: u31 = if (deadline) |d| t: { - const duration = d.durationFromNow(t_io); - if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i }; - break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds())); - } else max_poll_ms; - - const syscall = Syscall.start() catch |err| return .{ err, message_i }; - const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms); - syscall.finish(); - - switch (posix.errno(poll_rc)) { - .SUCCESS => { - if (poll_rc == 0) { - // Although spurious timeouts are OK, when no deadline - // is passed we must not return `error.Timeout`. - if (deadline == null) continue; - return .{ error.Timeout, message_i }; - } - continue :recv; - }, - .INTR => continue, - - .FAULT => |err| return .{ errnoBug(err), message_i }, - .INVAL => |err| return .{ errnoBug(err), message_i }, - .NOMEM => return .{ error.SystemResources, message_i }, - else => |err| return .{ posix.unexpectedErrno(err), message_i }, - } - }, - .INTR => continue, - - .BADF => |err| return .{ errnoBug(err), message_i }, - .NFILE => return .{ error.SystemFdQuotaExceeded, message_i }, - .MFILE => return .{ error.ProcessFdQuotaExceeded, message_i }, - .FAULT => |err| return .{ errnoBug(err), message_i }, - .INVAL => |err| return .{ errnoBug(err), message_i }, - .NOBUFS => return .{ error.SystemResources, message_i }, - .NOMEM => return .{ error.SystemResources, message_i }, - .NOTCONN => return .{ error.SocketUnconnected, message_i }, - .NOTSOCK => |err| return .{ errnoBug(err), message_i }, - .MSGSIZE => return .{ error.MessageOversize, message_i }, - .PIPE => return .{ error.SocketUnconnected, message_i }, - .OPNOTSUPP => |err| return .{ errnoBug(err), message_i }, - .CONNRESET => return .{ error.ConnectionResetByPeer, message_i }, - .NETDOWN => return .{ error.NetworkDown, message_i }, - else => |err| return .{ posix.unexpectedErrno(err), message_i }, + .NFILE => return syscall.fail(error.SystemFdQuotaExceeded), + .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded), + .NOBUFS => return syscall.fail(error.SystemResources), + .NOMEM => return syscall.fail(error.SystemResources), + .NOTCONN => return syscall.fail(error.SocketUnconnected), + .MSGSIZE => return syscall.fail(error.MessageOversize), + .PIPE => return syscall.fail(error.SocketUnconnected), + .CONNRESET => return syscall.fail(error.ConnectionResetByPeer), + .NETDOWN => return syscall.fail(error.NetworkDown), + .AGAIN => |err| return syscall.errnoBug(err), + .BADF => |err| return syscall.errnoBug(err), + .FAULT => |err| return syscall.errnoBug(err), + .INVAL => |err| return syscall.errnoBug(err), + .NOTSOCK => |err| return syscall.errnoBug(err), + .OPNOTSUPP => |err| return syscall.errnoBug(err), + else => |err| return syscall.unexpectedErrno(err), } } } fn netReceiveWindows( - userdata: ?*anyopaque, - handle: net.Socket.Handle, + t: *Threaded, + socket_handle: net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, flags: net.ReceiveFlags, - timeout: Io.Timeout, -) struct { ?net.Socket.ReceiveTimeoutError, usize } { +) struct { ?net.Socket.ReceiveError, usize } { if (!have_networking) return .{ error.NetworkDown, 0 }; - const t: *Threaded = @ptrCast(@alignCast(userdata)); _ = t; - _ = handle; + _ = socket_handle; _ = message_buffer; _ = data_buffer; _ = flags; - _ = timeout; @panic("TODO implement netReceiveWindows"); } diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig index 51a8d6647bd80bd28d37ffc43e725e38cfdc89d4..7a49996ff818fa3ea25b4941a99e28a5a404301c 100644 --- a/lib/std/Io/net.zig +++ b/lib/std/Io/net.zig @@ -1109,25 +1109,7 @@ pub const Socket = struct { if (n != messages.len) return err.?; } - pub const ReceiveError = error{ - /// Insufficient memory or other resource internal to the operating system. - SystemResources, - /// Per-process limit on the number of open file descriptors has been reached. - ProcessFdQuotaExceeded, - /// System-wide limit on the total number of open files has been reached. - SystemFdQuotaExceeded, - /// Local end has been shut down on a connection-oriented socket, or - /// the socket was never connected. - SocketUnconnected, - /// The socket type requires that message be sent atomically, and the - /// size of the message to be sent made this impossible. The message - /// was not transmitted, or was partially transmitted. - MessageOversize, - /// Network connection was unexpectedly closed by sender. - ConnectionResetByPeer, - /// The local network interface used to reach the destination is offline. - NetworkDown, - } || Io.UnexpectedError || Io.Cancelable; + pub const ReceiveError = Io.Operation.NetReceive.Error || Io.Cancelable; /// Waits for data. Connectionless. /// @@ -1145,7 +1127,7 @@ pub const Socket = struct { return message; } - pub const ReceiveTimeoutError = ReceiveError || Io.Timeout.Error; + pub const ReceiveTimeoutError = ReceiveError || Io.Timeout.Error || Io.ConcurrentError; /// Waits for data. Connectionless. /// @@ -1161,7 +1143,12 @@ pub const Socket = struct { timeout: Io.Timeout, ) ReceiveTimeoutError!IncomingMessage { var message: IncomingMessage = .init; - const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, timeout); + const maybe_err, const count = (try io.operateTimeout(.{ .net_receive = .{ + .socket = s.handle, + .message_buffer = (&message)[0..1], + .data_buffer = buffer, + .flags = .{}, + } }, timeout)).net_receive; if (maybe_err) |err| return err; assert(1 == count); return message; @@ -1186,7 +1173,13 @@ pub const Socket = struct { flags: ReceiveFlags, timeout: Io.Timeout, ) struct { ?ReceiveTimeoutError, usize } { - return io.vtable.netReceive(io.userdata, s.handle, message_buffer, data_buffer, flags, timeout); + const result = io.operateTimeout(.{ .net_receive = .{ + .socket_handle = s.handle, + .message_buffer = message_buffer, + .data_buffer = data_buffer, + .flags = flags, + } }, timeout) catch |err| return .{ err, 0 }; + return result.net_receive; } pub const CreatePairError = error{ -- 2.54.0 From 8b69341271db18a03a4aa7400a4c6679c5fbf5c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 Mar 2026 16:34:16 -0800 Subject: [PATCH 2/7] std.Io.Threaded: optimize batchAwaitConcurrent for net_receive Eagerly receive messages with MSG_DONTWAIT before polling. This makes the DNS resolution use case end up doing: recvmsg (EAGAIN) poll recvmsg (success) recvmsg (success) rather than: poll recvmsg (success) poll recvmsg (success) --- lib/std/Io/Threaded.zig | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 07d963b292ce0e2d449b212b61a130b75ec56901..e74450aa80f2a8be3d9005e6b514cdfac8ba4c16 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -2636,8 +2636,9 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper .net_receive => |*o| return .{ .net_receive = o: { if (!have_networking) break :o .{ error.NetworkDown, 0 }; if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags); - netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags) catch |err| switch (err) { + netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags, false) catch |err| switch (err) { error.Canceled => |e| return e, + error.WouldBlock => unreachable, else => |e| break :o .{ e, 0 }, }; break :o .{ null, 1 }; @@ -2846,19 +2847,41 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout { var index = b.submitted.head; while (index != .none) { - const submission = &b.storage[index.toIndex()].submission; + const storage = &b.storage[index.toIndex()]; + const submission = storage.submission; switch (submission.operation) { .file_read_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.ERR), .file_write_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.OUT | posix.POLL.ERR), .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR), - .net_receive => |o| try poll_storage.add(o.socket_handle, posix.POLL.IN | posix.POLL.ERR), + .net_receive => |*o| nb: { + var data_i: usize = 0; + const result: Io.Operation.Result = .{ .net_receive = for (o.message_buffer, 0..) |*msg, msg_i| { + const remaining_data_buffer = o.data_buffer[data_i..]; + netReceivePosix(o.socket_handle, msg, remaining_data_buffer, o.flags, true) catch |err| switch (err) { + error.Canceled => |e| return e, + error.WouldBlock => { + if (msg_i != 0) break .{ null, msg_i }; + try poll_storage.add(o.socket_handle, posix.POLL.IN | posix.POLL.ERR); + break :nb; + }, + else => |e| break .{ e, 0 }, + }; + data_i += msg.data.len; + } else .{ null, o.message_buffer.len } }; + switch (b.completed.tail) { + .none => b.completed.head = index, + else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index, + } + storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; + b.completed.tail = index; + }, } index = submission.node.next; } } switch (poll_storage.len) { 0 => return, - 1 => if (timeout == .none) { + 1 => if (timeout == .none and b.completed.head == .none) { const index = b.submitted.head; const storage = &b.storage[index.toIndex()]; const result = try operate(t, storage.submission.operation); @@ -13221,7 +13244,8 @@ fn netReceivePosix( message: *net.IncomingMessage, data_buffer: []u8, flags: net.ReceiveFlags, -) net.Socket.ReceiveError!void { + nonblocking: bool, +) (net.Socket.ReceiveError || error{WouldBlock})!void { // recvmmsg is useless, here's why: // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371) // * it wants iovecs for each message but we have a better API: one data @@ -13232,7 +13256,8 @@ fn netReceivePosix( @as(u32, if (flags.oob) posix.MSG.OOB else 0) | @as(u32, if (flags.peek) posix.MSG.PEEK else 0) | @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) | - posix.MSG.NOSIGNAL; + posix.MSG.NOSIGNAL | + @as(u32, if (nonblocking) posix.MSG.DONTWAIT else 0); var storage: PosixAddress = undefined; var iov: posix.iovec = .{ .base = data_buffer.ptr, .len = data_buffer.len }; @@ -13280,7 +13305,7 @@ fn netReceivePosix( .PIPE => return syscall.fail(error.SocketUnconnected), .CONNRESET => return syscall.fail(error.ConnectionResetByPeer), .NETDOWN => return syscall.fail(error.NetworkDown), - .AGAIN => |err| return syscall.errnoBug(err), + .AGAIN => return syscall.fail(error.WouldBlock), .BADF => |err| return syscall.errnoBug(err), .FAULT => |err| return syscall.errnoBug(err), .INVAL => |err| return syscall.errnoBug(err), -- 2.54.0 From c2ebbd8911d7eec337203bd1de1fd2f3a59273c8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 Mar 2026 17:46:07 -0800 Subject: [PATCH 3/7] std.Io.Threaded: implement net_receive for Windows --- lib/std/Io/Threaded.zig | 131 ++++++++++++++++++++++++++++++---- lib/std/os/windows/ws2_32.zig | 23 +++--- 2 files changed, 130 insertions(+), 24 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index e74450aa80f2a8be3d9005e6b514cdfac8ba4c16..cc83d6710c106abbc0cd19fc0cca556b62f849e8 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -2649,7 +2649,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); if (is_windows) { - batchDrainSubmittedWindows(b, false) catch |err| switch (err) { + batchDrainSubmittedWindows(t, b, false) catch |err| switch (err) { error.ConcurrencyUnavailable => unreachable, // passed concurrency=false else => |e| return e, }; @@ -2789,7 +2789,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout const t: *Threaded = @ptrCast(@alignCast(userdata)); if (is_windows) { const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t)); - try batchDrainSubmittedWindows(b, true); + try batchDrainSubmittedWindows(t, b, true); while (b.pending.head != .none and b.completed.head == .none) { var delay_interval: windows.LARGE_INTEGER = interval: { const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER); @@ -3005,6 +3005,31 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void { } } +fn batchCompleteBlockingWindows( + b: *Io.Batch, + operation_userdata: *WindowsBatchOperationUserdata, + result: Io.Operation.Result, +) void { + const erased_userdata = operation_userdata.toErased(); + const pending: *Io.Operation.Storage.Pending = @fieldParentPtr("userdata", erased_userdata); + switch (pending.node.prev) { + .none => b.pending.head = pending.node.next, + else => |prev_index| b.storage[prev_index.toIndex()].pending.node.next = pending.node.next, + } + switch (pending.node.next) { + .none => b.pending.tail = pending.node.prev, + else => |next_index| b.storage[next_index.toIndex()].pending.node.prev = pending.node.prev, + } + const storage: *Io.Operation.Storage = @fieldParentPtr("pending", pending); + const index: Io.Operation.OptionalIndex = .fromIndex(storage - b.storage.ptr); + switch (b.completed.tail) { + .none => b.completed.head = index, + else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index, + } + b.completed.tail = index; + storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; +} + fn batchApc( apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, @@ -3044,7 +3069,7 @@ fn batchApc( .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) }, .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) }, .device_io_control => .{ .device_io_control = iosb.* }, - .net_receive => unreachable, // TODO + .net_receive => unreachable, }; storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } }; }, @@ -3052,7 +3077,7 @@ fn batchApc( } /// If `concurrency` is false, `error.ConcurrencyUnavailable` is unreachable. -fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentError || Io.Cancelable)!void { +fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io.ConcurrentError || Io.Cancelable)!void { var index = b.submitted.head; errdefer b.submitted.head = index; while (index != .none) { @@ -3246,10 +3271,12 @@ fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentErr }; } }, - .net_receive => |o| { + .net_receive => |*o| { + // TODO integrate with overlapped I/O or equivalent to avoid this error if (concurrency) return error.ConcurrencyUnavailable; - _ = o; - @panic("TODO implement Batch NetReceive on Windows"); + batchCompleteBlockingWindows(b, operation_userdata, .{ + .net_receive = netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags), + }); }, } index = submission.node.next; @@ -13323,13 +13350,89 @@ fn netReceiveWindows( data_buffer: []u8, flags: net.ReceiveFlags, ) struct { ?net.Socket.ReceiveError, usize } { - if (!have_networking) return .{ error.NetworkDown, 0 }; - _ = t; - _ = socket_handle; - _ = message_buffer; - _ = data_buffer; - _ = flags; - @panic("TODO implement netReceiveWindows"); + netReceiveWindowsOne(t, socket_handle, &message_buffer[0], data_buffer, flags) catch |err| return .{ err, 0 }; + return .{ null, 1 }; +} + +fn netReceiveWindowsOne( + t: *Threaded, + socket_handle: net.Socket.Handle, + message: *net.IncomingMessage, + data_buffer: []u8, + flags: net.ReceiveFlags, +) net.Socket.ReceiveError!void { + comptime assert(have_networking); + + var windows_flags: u32 = + @as(u32, if (flags.oob) ws2_32.MSG.OOB else 0) | + @as(u32, if (flags.peek) ws2_32.MSG.PEEK else 0) | + @as(u32, if (flags.trunc) ws2_32.MSG.TRUNC else 0); + + var buf: ws2_32.WSABUF = .{ + .buf = data_buffer.ptr, + .len = std.math.cast(u32, data_buffer.len) orelse return error.MessageOversize, + }; + var n: u32 = undefined; + var syscall: Syscall = try .start(); + var from_storage: WsaAddress = undefined; + var from_storage_len: i32 = @sizeOf(WsaAddress); + + while (true) { + const rc = ws2_32.WSARecvFrom( + socket_handle, + (&buf)[0..1], + 1, + &n, + &windows_flags, + &from_storage.any, + &from_storage_len, + null, + null, + ); + if (rc != ws2_32.SOCKET_ERROR) { + syscall.finish(); + message.* = .{ + .from = addressFromWsa(&from_storage), + .data = data_buffer[0..n], + .control = &.{}, + .flags = .{ + .eor = false, + .trunc = (windows_flags & ws2_32.MSG.TRUNC) != 0, + .ctrunc = (windows_flags & ws2_32.MSG.CTRUNC) != 0, + .oob = false, + .errqueue = false, + }, + }; + return; + } + switch (ws2_32.WSAGetLastError()) { + .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => { + try syscall.checkCancel(); + continue; + }, + .NOTINITIALISED => { + syscall.finish(); + try initializeWsa(t); + syscall = try .start(); + continue; + }, + + .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer), + .ENETDOWN => return syscall.fail(error.NetworkDown), + .ENETRESET => return syscall.fail(error.ConnectionResetByPeer), + .ENOTCONN => return syscall.fail(error.SocketUnconnected), + .EFAULT => unreachable, // a pointer is not completely contained in user address space. + + else => |err| { + syscall.finish(); + switch (err) { + .EINVAL => return wsaErrorBug(err), + .EMSGSIZE => return wsaErrorBug(err), + else => return windows.unexpectedWSAError(err), + } + }, + } + } } fn netReceiveUnavailable( diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index d5c74e22123056de1719a5d5e433b0047cf5bcf9..0abfe64ec310a04542323e07f8813b6509f859c5 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/lib/std/os/windows/ws2_32.zig @@ -661,17 +661,20 @@ pub const IOC_OUT = 1073741824; pub const IOC_IN = 2147483648; pub const MSG = struct { - pub const TRUNC = 256; - pub const CTRUNC = 512; - pub const BCAST = 1024; - pub const MCAST = 2048; - pub const ERRQUEUE = 4096; + pub const OOB = 0x1; + pub const PEEK = 0x2; + pub const DONTROUTE = 0x4; + pub const WAITALL = 0x8; + pub const INTERRUPT = 0x10; + pub const PUSH_IMMEDIATE = 0x20; + + pub const TRUNC = 0x0100; + pub const CTRUNC = 0x0200; + pub const BCAST = 0x0400; + pub const MCAST = 0x0800; + + pub const PARTIAL = 0x8000; - pub const PEEK = 2; - pub const WAITALL = 8; - pub const PUSH_IMMEDIATE = 32; - pub const PARTIAL = 32768; - pub const INTERRUPT = 16; pub const MAXIOVLEN = 16; }; -- 2.54.0 From 4cbf30c2a419df3181b41cb0783e175c54248de2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 Mar 2026 18:04:35 -0800 Subject: [PATCH 4/7] std.Io.Uring: implement net_receive operate Batch implementation still TODO. --- lib/std/Io/Threaded.zig | 17 ---------- lib/std/Io/Uring.zig | 71 +++++++++++++---------------------------- 2 files changed, 22 insertions(+), 66 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index cc83d6710c106abbc0cd19fc0cca556b62f849e8..1d12f8a55b1a6737c21d9c76f366fdcd01cfe2e7 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -13435,23 +13435,6 @@ fn netReceiveWindowsOne( } } -fn netReceiveUnavailable( - userdata: ?*anyopaque, - handle: net.Socket.Handle, - message_buffer: []net.IncomingMessage, - data_buffer: []u8, - flags: net.ReceiveFlags, - timeout: Io.Timeout, -) struct { ?net.Socket.ReceiveTimeoutError, usize } { - _ = userdata; - _ = handle; - _ = message_buffer; - _ = data_buffer; - _ = flags; - _ = timeout; - return .{ error.NetworkDown, 0 }; -} - fn netWritePosix( userdata: ?*anyopaque, fd: net.Socket.Handle, diff --git a/lib/std/Io/Uring.zig b/lib/std/Io/Uring.zig index 843c1a0c3c179d9d7562190986fc8e75d44fd558..13c52fc3d7851fa2dd30a0ee572cf53731c9cb55 100644 --- a/lib/std/Io/Uring.zig +++ b/lib/std/Io/Uring.zig @@ -777,7 +777,6 @@ pub fn io(ev: *Evented) Io { .netConnectUnix = netConnectUnixUnavailable, .netSocketCreatePair = netSocketCreatePairUnavailable, .netSend = netSendUnavailable, - .netReceive = netReceive, .netRead = netReadUnavailable, .netWrite = netWriteUnavailable, .netWriteFile = netWriteFileUnavailable, @@ -2092,6 +2091,18 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper .device_io_control => |o| .{ .device_io_control = try ev.deviceIoControl(try maybe_sync.enterSync(ev), o), }, + .net_receive => |o| .{ + .net_receive = r: { + const opt_err, const n = ev.netReceive(&maybe_sync.cancel_region, o.socket_handle, o.message_buffer, o.data_buffer, o.flags); + break :r .{ + if (opt_err) |err| switch (err) { + error.Canceled => |e| return e, + else => |e| e, + } else null, + n, + }; + }, + }, }; } @@ -2375,6 +2386,10 @@ fn batchDrainSubmitted( return error.ConcurrencyUnavailable else .{ .device_io_control = try ev.deviceIoControl(try maybe_sync.enterSync(ev), o) }, + .net_receive => |o| { + _ = o; + @panic("TODO implement batchDrainSubmitted for net_receive"); + }, })) |result| { switch (batch.completed.tail) { .none => batch.completed.head = index, @@ -2475,6 +2490,7 @@ fn batchDrainReady(batch: *Io.Batch) Io.Timeout.Error!void { }, }, .device_io_control => unreachable, + .net_receive => @panic("TODO"), })) |result| { switch (batch.completed.tail) { .none => batch.completed.head = index, @@ -5035,37 +5051,16 @@ fn netSendUnavailable( } fn netReceive( - userdata: ?*anyopaque, + ev: *Evented, + cancel_region: *CancelRegion, handle: net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, flags: net.ReceiveFlags, - timeout: Io.Timeout, -) struct { ?net.Socket.ReceiveTimeoutError, usize } { - const ev: *Evented = @ptrCast(@alignCast(userdata)); - const ev_io = ev.io(); - +) struct { ?net.Socket.ReceiveError, usize } { var message_i: usize = 0; var data_i: usize = 0; - const deadline: ?struct { - raw: Io.Timestamp, - timespec: linux.kernel_timespec, - clock: Io.Clock, - } = if (timeout.toTimestamp(ev_io)) |deadline| deadline: { - const ns = deadline.raw.toNanoseconds(); - break :deadline .{ - .raw = deadline.raw, - .timespec = .{ - .sec = @intCast(@divFloor(ns, std.time.ns_per_s)), - .nsec = @intCast(@mod(ns, std.time.ns_per_s)), - }, - .clock = deadline.clock, - }; - } else null; - - var cancel_region: CancelRegion = .init(); - defer cancel_region.deinit(); while (true) { if (message_buffer.len - message_i == 0) return .{ null, message_i }; const message = &message_buffer[message_i]; @@ -5085,7 +5080,7 @@ fn netReceive( const thread = cancel_region.awaitIoUring() catch |err| return .{ err, message_i }; thread.enqueue().* = .{ .opcode = .RECVMSG, - .flags = if (deadline) |_| linux.IOSQE_IO_LINK else 0, + .flags = 0, .ioprio = 0, .fd = handle, .off = 0, @@ -5102,26 +5097,6 @@ fn netReceive( .addr3 = 0, .resv = 0, }; - if (deadline) |*deadline_ptr| thread.enqueue().* = .{ - .opcode = .LINK_TIMEOUT, - .flags = linux.IOSQE_CQE_SKIP_SUCCESS, - .ioprio = 0, - .fd = 0, - .off = 0, - .addr = @intFromPtr(&deadline_ptr.timespec), - .len = 1, - .rw_flags = linux.IORING_TIMEOUT_ABS | @as(u32, switch (deadline_ptr.clock) { - .real => linux.IORING_TIMEOUT_REALTIME, - else => 0, - .boot => linux.IORING_TIMEOUT_BOOTTIME, - }), - .user_data = @intFromEnum(Completion.Userdata.wakeup), - .buf_index = 0, - .personality = 0, - .splice_fd_in = 0, - .addr3 = 0, - .resv = 0, - }; ev.yield(null, .nothing); const completion = cancel_region.completion(); switch (completion.errno()) { @@ -5144,9 +5119,7 @@ fn netReceive( continue; }, .AGAIN => unreachable, - .INTR, .CANCELED => if (deadline) |d| if (now(ev, d.clock).nanoseconds >= d.raw.nanoseconds) - return .{ error.Timeout, message_i }, - + .INTR, .CANCELED => {}, .BADF => |err| return .{ errnoBug(err), message_i }, .NFILE => return .{ error.SystemFdQuotaExceeded, message_i }, .MFILE => return .{ error.ProcessFdQuotaExceeded, message_i }, -- 2.54.0 From dd8de03720588eaaa165da70295e29dd23fe1886 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 Mar 2026 18:08:36 -0800 Subject: [PATCH 5/7] std.Io.Dispatch: fix compile errors --- lib/std/Io/Dispatch.zig | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/lib/std/Io/Dispatch.zig b/lib/std/Io/Dispatch.zig index a9bd960a342976cf252681d641cd14ae9c17c77c..1bc5195565c0452ef0363ce561c2609eba4cb5fb 100644 --- a/lib/std/Io/Dispatch.zig +++ b/lib/std/Io/Dispatch.zig @@ -459,7 +459,6 @@ pub fn io(ev: *Evented) Io { .netConnectUnix = netConnectUnixUnavailable, .netSocketCreatePair = netSocketCreatePairUnavailable, .netSend = netSendUnavailable, - .netReceive = netReceiveUnavailable, .netRead = netReadUnavailable, .netWrite = netWriteUnavailable, .netWriteFile = netWriteFileUnavailable, @@ -1714,6 +1713,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper }, }, .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) }, + .net_receive => @panic("TODO implement net_receive operation"), } } @@ -2134,6 +2134,7 @@ fn batchDrainSubmitted( break :result null; }, .device_io_control => {}, + .net_receive => @panic("TODO implement batched net_receive"), }; if (concurrency) return error.ConcurrencyUnavailable; break :result try operate(ev, storage.submission.operation); @@ -2192,6 +2193,7 @@ fn batchSourceEvent(context: ?*anyopaque) callconv(.c) void { } }; }, .device_io_control => unreachable, + .net_receive => @panic("TODO implement batched net_receive"), }; switch (pending.node.prev) { @@ -4872,24 +4874,6 @@ fn netSendUnavailable( return .{ error.NetworkDown, 0 }; } -fn netReceiveUnavailable( - userdata: ?*anyopaque, - handle: net.Socket.Handle, - message_buffer: []net.IncomingMessage, - data_buffer: []u8, - flags: net.ReceiveFlags, - timeout: Io.Timeout, -) struct { ?net.Socket.ReceiveTimeoutError, usize } { - const ev: *Evented = @ptrCast(@alignCast(userdata)); - _ = ev; - _ = handle; - _ = message_buffer; - _ = data_buffer; - _ = flags; - _ = timeout; - return .{ error.NetworkDown, 0 }; -} - fn netReadUnavailable( userdata: ?*anyopaque, fd: net.Socket.Handle, -- 2.54.0 From 85ed81bb94ca59da49a128cb6ee5065b130ba06c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 8 Mar 2026 16:12:37 -0700 Subject: [PATCH 6/7] std.Io.Threaded: implement netReceive for Windows --- lib/std/Io/Threaded.zig | 75 ++++++++++++++++++++++++++++++++++++++--- lib/std/Io/net.zig | 15 +++++---- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 1d12f8a55b1a6737c21d9c76f366fdcd01cfe2e7..81a500e0f980e3d3c14288fe84ca7a815d8c9fac 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -13072,11 +13072,76 @@ fn netSendWindows( ) struct { ?net.Socket.SendError, usize } { if (!have_networking) return .{ error.NetworkDown, 0 }; const t: *Threaded = @ptrCast(@alignCast(userdata)); - _ = t; - _ = handle; - _ = messages; - _ = flags; - @panic("TODO netSendWindows"); + + // Ignored flags: confirm, eor, fastopen + const windows_flags: u32 = + @as(u32, if (flags.oob) ws2_32.MSG.OOB else 0) | + @as(u32, if (flags.dont_route) ws2_32.MSG.DONTROUTE else 0); + + for (messages, 0..) |*m, i| { + netSendWindowsOne(t, handle, m, windows_flags) catch |err| return .{ err, i }; + } + return .{ null, messages.len }; +} + +fn netSendWindowsOne( + t: *Threaded, + handle: net.Socket.Handle, + message: *net.OutgoingMessage, + flags: u32, +) net.Socket.SendError!void { + var buf: ws2_32.WSABUF = .{ + .buf = @constCast(message.data_ptr), + .len = std.math.cast(u32, message.data_len) orelse return error.MessageOversize, + }; + var n: u32 = undefined; + var address: WsaAddress = undefined; + const address_size = addressToWsa(message.address, &address); + var syscall: Syscall = try .start(); + while (true) { + const rc = ws2_32.WSASendTo( + handle, + (&buf)[0..1], + 1, + &n, + flags, + &address.any, + address_size, + null, + null, + ); + if (rc != ws2_32.SOCKET_ERROR) { + syscall.finish(); + return; + } + switch (ws2_32.WSAGetLastError()) { + .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => { + try syscall.checkCancel(); + continue; + }, + .NOTINITIALISED => { + syscall.finish(); + try initializeWsa(t); + syscall = try .start(); + continue; + }, + + .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer), + .ENETDOWN => return syscall.fail(error.NetworkDown), + .ENETRESET => return syscall.fail(error.ConnectionResetByPeer), + .ENOTCONN => return syscall.fail(error.SocketUnconnected), + .EFAULT => unreachable, // a pointer is not completely contained in user address space. + + else => |err| { + syscall.finish(); + switch (err) { + .EINVAL => return wsaErrorBug(err), + .EMSGSIZE => return wsaErrorBug(err), + else => return windows.unexpectedWSAError(err), + } + }, + } + } } fn netSendUnavailable( diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig index 7a49996ff818fa3ea25b4941a99e28a5a404301c..6d5b11e1b10907be28241c63c1f611204291d287 100644 --- a/lib/std/Io/net.zig +++ b/lib/std/Io/net.zig @@ -1117,12 +1117,13 @@ pub const Socket = struct { /// * `receiveTimeout` pub fn receive(s: *const Socket, io: Io, buffer: []u8) ReceiveError!IncomingMessage { var message: IncomingMessage = .init; - const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, .none); - if (maybe_err) |err| switch (err) { - // No timeout is passed to `netReceieve`, so it must not return timeout related errors. - error.Timeout => unreachable, - else => |e| return e, - }; + const maybe_err, const count = (try io.operate(.{ .net_receive = .{ + .socket_handle = s.handle, + .message_buffer = (&message)[0..1], + .data_buffer = buffer, + .flags = .{}, + } })).net_receive; + if (maybe_err) |err| return err; assert(1 == count); return message; } @@ -1144,7 +1145,7 @@ pub const Socket = struct { ) ReceiveTimeoutError!IncomingMessage { var message: IncomingMessage = .init; const maybe_err, const count = (try io.operateTimeout(.{ .net_receive = .{ - .socket = s.handle, + .socket_handle = s.handle, .message_buffer = (&message)[0..1], .data_buffer = buffer, .flags = .{}, -- 2.54.0 From 80625990d5ce82b781de54c4587b489cbd2cd55f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 Mar 2026 19:24:55 -0800 Subject: [PATCH 7/7] std: different mechanism for disabling network dependency On Windows, it is sometimes problematic to depend on ws2_32.dll. Before, users of std.Io.Threaded would have to call ioBasic() rather than io() in order to avoid unnecessary dependencies on ws2_32.dll. Now, the application can disable networking with std.Options. This change is necessary due to moving networking functionality to be based on Io.Operation, which is a tagged union. --- lib/compiler/test_runner.zig | 2 +- lib/compiler_rt.zig | 2 +- lib/fuzzer.zig | 2 +- lib/std/Io/Threaded.zig | 166 ++---------------- lib/std/std.zig | 5 +- lib/ubsan_rt.zig | 4 + test/incremental/add_decl | 12 +- test/incremental/add_decl_namespaced | 12 +- test/incremental/bad_import | 4 +- test/incremental/change_embed_file | 6 +- test/incremental/change_enum_tag_type | 6 +- test/incremental/change_exports | 12 +- test/incremental/change_fn_type | 6 +- test/incremental/change_generic_line_number | 4 +- test/incremental/change_line_number | 4 +- test/incremental/change_panic_handler | 6 +- .../incremental/change_panic_handler_explicit | 6 +- test/incremental/change_shift_op | 4 +- test/incremental/change_struct_same_fields | 6 +- test/incremental/change_zon_file | 6 +- .../change_zon_file_no_result_type | 2 +- test/incremental/compile_log | 6 +- test/incremental/fix_astgen_failure | 6 +- test/incremental/function_becomes_inline | 6 +- test/incremental/hello | 4 +- test/incremental/make_decl_pub | 4 +- test/incremental/modify_inline_fn | 4 +- test/incremental/move_src | 4 +- .../incremental/no_change_preserves_tag_names | 4 +- .../recursive_function_becomes_non_recursive | 4 +- test/incremental/remove_enum_field | 4 +- test/incremental/unreferenced_error | 8 +- test/standalone/coff_dwarf/build.zig | 3 + test/standalone/dirname/exists_in.zig | 2 +- test/standalone/dirname/touch.zig | 2 +- test/standalone/issue_5825/build.zig | 1 + test/standalone/mix_o_files/build.zig | 3 + test/standalone/run_cwd/check_file_exists.zig | 2 +- test/standalone/shared_library/build.zig | 5 +- test/standalone/windows_argv/build.zig | 3 + 40 files changed, 122 insertions(+), 230 deletions(-) diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index 9c100176d502b82277d1c99a6688473276746240..899b5dafbfb7fa2683854b81ff198eb2a7be4573 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -17,7 +17,7 @@ var fba: std.heap.FixedBufferAllocator = .init(&fba_buffer); var fba_buffer: [8192]u8 = undefined; var stdin_buffer: [4096]u8 = undefined; var stdout_buffer: [4096]u8 = undefined; -const runner_threaded_io: Io = Io.Threaded.global_single_threaded.ioBasic(); +const runner_threaded_io: Io = Io.Threaded.global_single_threaded.io(); /// Keep in sync with logic in `std.Build.addRunArtifact` which decides whether /// the test runner will communicate with the build runner via `std.zig.Server`. diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 5c9c7a59af3738ac49d7912b1a1c8de6c7802c59..ce8f49d17b9e3157fdf2c13a4c164faffc12901a 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -17,7 +17,7 @@ else null; pub const std_options_debug_io: std.Io = if (builtin.is_test) - std.Io.Threaded.global_single_threaded.ioBasic() + std.Io.Threaded.global_single_threaded.io() else unreachable; diff --git a/lib/fuzzer.zig b/lib/fuzzer.zig index b3a6eb5196c1fbad6c2af380d815737892ef5866..c97aeea4e8a7d35be44c4607a2b3b268e0897d0b 100644 --- a/lib/fuzzer.zig +++ b/lib/fuzzer.zig @@ -13,7 +13,7 @@ pub const std_options = std.Options{ .logFn = logOverride, }; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); fn logOverride( comptime level: std.log.Level, diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 81a500e0f980e3d3c14288fe84ca7a815d8c9fac..59382348e9dcf75313fbc1e55e97dbf907f5091d 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -1908,142 +1908,10 @@ pub fn io(t: *Threaded) Io { }; } -/// Same as `io` but disables all networking functionality, which has -/// an additional dependency on Windows (ws2_32). -pub fn ioBasic(t: *Threaded) Io { - return .{ - .userdata = t, - .vtable = &.{ - .crashHandler = crashHandler, - - .async = async, - .concurrent = concurrent, - .await = await, - .cancel = cancel, - - .groupAsync = groupAsync, - .groupConcurrent = groupConcurrent, - .groupAwait = groupAwait, - .groupCancel = groupCancel, - - .recancel = recancel, - .swapCancelProtection = swapCancelProtection, - .checkCancel = checkCancel, - - .futexWait = futexWait, - .futexWaitUncancelable = futexWaitUncancelable, - .futexWake = futexWake, - - .operate = operate, - .batchAwaitAsync = batchAwaitAsync, - .batchAwaitConcurrent = batchAwaitConcurrent, - .batchCancel = batchCancel, - - .dirCreateDir = dirCreateDir, - .dirCreateDirPath = dirCreateDirPath, - .dirCreateDirPathOpen = dirCreateDirPathOpen, - .dirStat = dirStat, - .dirStatFile = dirStatFile, - .dirAccess = dirAccess, - .dirCreateFile = dirCreateFile, - .dirCreateFileAtomic = dirCreateFileAtomic, - .dirOpenFile = dirOpenFile, - .dirOpenDir = dirOpenDir, - .dirClose = dirClose, - .dirRead = dirRead, - .dirRealPath = dirRealPath, - .dirRealPathFile = dirRealPathFile, - .dirDeleteFile = dirDeleteFile, - .dirDeleteDir = dirDeleteDir, - .dirRename = dirRename, - .dirRenamePreserve = dirRenamePreserve, - .dirSymLink = dirSymLink, - .dirReadLink = dirReadLink, - .dirSetOwner = dirSetOwner, - .dirSetFileOwner = dirSetFileOwner, - .dirSetPermissions = dirSetPermissions, - .dirSetFilePermissions = dirSetFilePermissions, - .dirSetTimestamps = dirSetTimestamps, - .dirHardLink = dirHardLink, - - .fileStat = fileStat, - .fileLength = fileLength, - .fileClose = fileClose, - .fileWritePositional = fileWritePositional, - .fileWriteFileStreaming = fileWriteFileStreaming, - .fileWriteFilePositional = fileWriteFilePositional, - .fileReadPositional = fileReadPositional, - .fileSeekBy = fileSeekBy, - .fileSeekTo = fileSeekTo, - .fileSync = fileSync, - .fileIsTty = fileIsTty, - .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes, - .fileSupportsAnsiEscapeCodes = fileSupportsAnsiEscapeCodes, - .fileSetLength = fileSetLength, - .fileSetOwner = fileSetOwner, - .fileSetPermissions = fileSetPermissions, - .fileSetTimestamps = fileSetTimestamps, - .fileLock = fileLock, - .fileTryLock = fileTryLock, - .fileUnlock = fileUnlock, - .fileDowngradeLock = fileDowngradeLock, - .fileRealPath = fileRealPath, - .fileHardLink = fileHardLink, - - .fileMemoryMapCreate = fileMemoryMapCreate, - .fileMemoryMapDestroy = fileMemoryMapDestroy, - .fileMemoryMapSetLength = fileMemoryMapSetLength, - .fileMemoryMapRead = fileMemoryMapRead, - .fileMemoryMapWrite = fileMemoryMapWrite, - - .processExecutableOpen = processExecutableOpen, - .processExecutablePath = processExecutablePath, - .lockStderr = lockStderr, - .tryLockStderr = tryLockStderr, - .unlockStderr = unlockStderr, - .processCurrentPath = processCurrentPath, - .processSetCurrentDir = processSetCurrentDir, - .processSetCurrentPath = processSetCurrentPath, - .processReplace = processReplace, - .processReplacePath = processReplacePath, - .processSpawn = processSpawn, - .processSpawnPath = processSpawnPath, - .childWait = childWait, - .childKill = childKill, - - .progressParentFile = progressParentFile, - - .now = now, - .clockResolution = clockResolution, - .sleep = sleep, - - .random = random, - .randomSecure = randomSecure, - - .netListenIp = netListenIpUnavailable, - .netListenUnix = netListenUnixUnavailable, - .netAccept = netAcceptUnavailable, - .netBindIp = netBindIpUnavailable, - .netConnectIp = netConnectIpUnavailable, - .netSocketCreatePair = netSocketCreatePairUnavailable, - .netConnectUnix = netConnectUnixUnavailable, - .netClose = netCloseUnavailable, - .netShutdown = netShutdownUnavailable, - .netRead = netReadUnavailable, - .netWrite = netWriteUnavailable, - .netWriteFile = netWriteFileUnavailable, - .netSend = netSendUnavailable, - .netInterfaceNameResolve = netInterfaceNameResolveUnavailable, - .netInterfaceName = netInterfaceNameUnavailable, - .netLookup = netLookupUnavailable, - }, - }; -} - pub const socket_flags_unsupported = is_darwin or native_os == .haiku; const have_accept4 = !socket_flags_unsupported; const have_flock_open_flags = @hasField(posix.O, "EXLOCK"); -const have_networking = native_os != .wasi; +const have_networking = std.options.networking and native_os != .wasi; const have_flock = @TypeOf(posix.system.flock) != void; const have_sendmmsg = native_os == .linux; const have_futex = switch (builtin.cpu.arch) { @@ -2595,7 +2463,7 @@ fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io. return; } const t: *Threaded = @ptrCast(@alignCast(userdata)); - const t_io = ioBasic(t); + const t_io = io(t); const timeout_ns: ?u64 = ns: { const d = timeout.toDurationFromNow(t_io) orelse break :ns null; break :ns std.math.lossyCast(u64, d.raw.toNanoseconds()); @@ -2788,7 +2656,7 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.AwaitConcurrentError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); if (is_windows) { - const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t)); + const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(io(t)); try batchDrainSubmittedWindows(t, b, true); while (b.pending.head != .none and b.completed.head == .none) { var delay_interval: windows.LARGE_INTEGER = interval: { @@ -2898,7 +2766,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout }, else => {}, } - const t_io = ioBasic(t); + const t_io = io(t); const deadline = timeout.toTimestamp(t_io); while (true) { const timeout_ms: i32 = t: { @@ -3536,7 +3404,7 @@ fn dirCreateDirPathOpenPosix( options: Dir.OpenOptions, ) Dir.CreateDirPathOpenError!Dir { const t: *Threaded = @ptrCast(@alignCast(userdata)); - const t_io = ioBasic(t); + const t_io = io(t); return dirOpenDirPosix(t, dir, sub_path, options) catch |err| switch (err) { error.FileNotFound => { _ = try dir.createDirPathStatus(t_io, sub_path, permissions); @@ -3657,7 +3525,7 @@ fn dirCreateDirPathOpenWasi( options: Dir.OpenOptions, ) Dir.CreateDirPathOpenError!Dir { const t: *Threaded = @ptrCast(@alignCast(userdata)); - const t_io = ioBasic(t); + const t_io = io(t); return dirOpenDirWasi(t, dir, sub_path, options) catch |err| switch (err) { error.FileNotFound => { _ = try dir.createDirPathStatus(t_io, sub_path, permissions); @@ -4698,7 +4566,7 @@ fn dirCreateFileAtomic( options: Dir.CreateFileAtomicOptions, ) Dir.CreateFileAtomicError!File.Atomic { const t: *Threaded = @ptrCast(@alignCast(userdata)); - const t_io = ioBasic(t); + const t_io = io(t); // Linux has O_TMPFILE, but linkat() does not support AT_REPLACE, so it's // useless when we have to make up a bogus path name to do the rename() @@ -10326,19 +10194,19 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &n); if (rc != 0) return error.NameTooLong; const symlink_path = std.mem.sliceTo(&symlink_path_buf, 0); - return Io.Dir.realPathFileAbsolute(ioBasic(t), symlink_path, out_buffer) catch |err| switch (err) { + return Io.Dir.realPathFileAbsolute(io(t), symlink_path, out_buffer) catch |err| switch (err) { error.NetworkNotFound => unreachable, // Windows-only error.FileBusy => unreachable, // Windows-only else => |e| return e, }; }, - .linux, .serenity => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/exe", out_buffer) catch |err| switch (err) { + .linux, .serenity => return Io.Dir.readLinkAbsolute(io(t), "/proc/self/exe", out_buffer) catch |err| switch (err) { error.UnsupportedReparsePointType => unreachable, // Windows-only error.NetworkNotFound => unreachable, // Windows-only error.FileBusy => unreachable, // Windows-only else => |e| return e, }, - .illumos => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) { + .illumos => return Io.Dir.readLinkAbsolute(io(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) { error.UnsupportedReparsePointType => unreachable, // Windows-only error.NetworkNotFound => unreachable, // Windows-only error.FileBusy => unreachable, // Windows-only @@ -11700,7 +11568,7 @@ fn sleepPosix(timeout: Io.Timeout) Io.Cancelable!void { } fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void { - const t_io = ioBasic(t); + const t_io = io(t); const w = std.os.wasi; const clock: w.subscription_clock_t = if (timeout.toDurationFromNow(t_io)) |d| .{ @@ -11729,7 +11597,7 @@ fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void { } fn sleepNanosleep(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void { - const t_io = ioBasic(t); + const t_io = io(t); const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type; const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type; @@ -11961,6 +11829,7 @@ fn netListenUnixWindows( options: net.UnixAddress.ListenOptions, ) net.UnixAddress.ListenError!net.Socket.Handle { if (!net.has_unix_sockets) return error.AddressFamilyUnsupported; + if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); const socket_handle = openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) { @@ -12457,6 +12326,7 @@ fn netConnectUnixWindows( address: *const net.UnixAddress, ) net.UnixAddress.ConnectError!net.Socket.Handle { if (!net.has_unix_sockets) return error.AddressFamilyUnsupported; + if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); const socket_handle = try openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream }); @@ -13426,7 +13296,7 @@ fn netReceiveWindowsOne( data_buffer: []u8, flags: net.ReceiveFlags, ) net.Socket.ReceiveError!void { - comptime assert(have_networking); + if (!have_networking) return error.NetworkDown; var windows_flags: u32 = @as(u32, if (flags.oob) ws2_32.MSG.OOB else 0) | @@ -13601,6 +13471,7 @@ fn netWriteWindows( data: []const []const u8, splat: usize, ) net.Stream.Writer.Error!usize { + if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); comptime assert(is_windows); @@ -13723,6 +13594,7 @@ fn addBuf(v: []posix.iovec_const, i: *iovlen_t, bytes: []const u8) void { } fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void { + if (!have_networking) unreachable; const t: *Threaded = @ptrCast(@alignCast(userdata)); _ = t; switch (native_os) { @@ -13931,7 +13803,7 @@ fn netLookupUnavailable( _ = host_name; _ = options; const t: *Threaded = @ptrCast(@alignCast(userdata)); - resolved.close(ioBasic(t)); + resolved.close(io(t)); return error.NetworkDown; } @@ -14214,7 +14086,7 @@ fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Can fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { if (!t.stderr_writer_initialized) { - const io_t = ioBasic(t); + const io_t = io(t); if (is_windows) t.stderr_writer.file = .stderr(); t.stderr_writer.io = io_t; t.stderr_writer_initialized = true; diff --git a/lib/std/std.zig b/lib/std/std.zig index d563cdfae7aac1003fb7d91472cd6653f78a5cee..c25241975aee08701fb8c87d3b6f2af2411ad5c5 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -174,6 +174,9 @@ pub const Options = struct { /// stack traces will just print an error to the relevant `Io.Writer` and return. allow_stack_tracing: bool = !@import("builtin").strip_debug_info, + /// Allows disabling networking in std.Io implementations. + networking: bool = true, + /// TODO This is a separate decl instead of a field as a workaround around /// compilation errors due to zig not being lazy enough. pub const logTerminalMode: fn () Io.Terminal.Mode = log.defaultTerminalMode; @@ -202,7 +205,7 @@ pub const Options = struct { /// implementation based on coroutines, one likely wants `std.debug.print` /// to directly write to stderr without trying to interact with the code /// being debugged. - pub const debug_io: Io = if (@hasDecl(root, "std_options_debug_io")) root.std_options_debug_io else debug_threaded_io.?.ioBasic(); + pub const debug_io: Io = if (@hasDecl(root, "std_options_debug_io")) root.std_options_debug_io else debug_threaded_io.?.io(); /// Overrides `std.Io.File.Permissions`. pub const FilePermissions: ?type = if (@hasDecl(root, "std_options_FilePermissions")) root.std_options_FilePermissions else null; diff --git a/lib/ubsan_rt.zig b/lib/ubsan_rt.zig index aa5942a0bda0d39229a00266284fa47211abd80c..59278a91161d4ef780539de147c94d54a67aa320 100644 --- a/lib/ubsan_rt.zig +++ b/lib/ubsan_rt.zig @@ -3,6 +3,10 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const panic = std.debug.panicExtra; +pub const std_options: std.Options = .{ + .networking = false, +}; + const SourceLocation = extern struct { file_name: ?[*:0]const u8, line: u32, diff --git a/test/incremental/add_decl b/test/incremental/add_decl index 99f6b2d34fb9c244caac9d0741f7511b6d279bbc..662160fc2dc1038babaab18a234d0917d82675bc 100644 --- a/test/incremental/add_decl +++ b/test/incremental/add_decl @@ -10,7 +10,7 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, foo); } const foo = "good morning\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good morning\n" #update=add new declaration @@ -21,7 +21,7 @@ pub fn main() !void { } const foo = "good morning\n"; const bar = "good evening\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good morning\n" #update=reference new declaration @@ -32,7 +32,7 @@ pub fn main() !void { } const foo = "good morning\n"; const bar = "good evening\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good evening\n" #update=reference missing declaration @@ -43,7 +43,7 @@ pub fn main() !void { } const foo = "good morning\n"; const bar = "good evening\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:3:52: error: use of undeclared identifier 'qux' #update=add missing declaration @@ -55,7 +55,7 @@ pub fn main() !void { const foo = "good morning\n"; const bar = "good evening\n"; const qux = "good night\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good night\n" #update=remove unused declarations @@ -65,5 +65,5 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, qux); } const qux = "good night\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good night\n" diff --git a/test/incremental/add_decl_namespaced b/test/incremental/add_decl_namespaced index 6fc22f10b9411cbeaa988618b0f2512d96bcf652..aed78815c81aec020f3a4e4dc6a7bbb8c0054ffe 100644 --- a/test/incremental/add_decl_namespaced +++ b/test/incremental/add_decl_namespaced @@ -10,7 +10,7 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, @This().foo); } const foo = "good morning\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good morning\n" #update=add new declaration @@ -21,7 +21,7 @@ pub fn main() !void { } const foo = "good morning\n"; const bar = "good evening\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good morning\n" #update=reference new declaration @@ -32,7 +32,7 @@ pub fn main() !void { } const foo = "good morning\n"; const bar = "good evening\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good evening\n" #update=reference missing declaration @@ -43,7 +43,7 @@ pub fn main() !void { } const foo = "good morning\n"; const bar = "good evening\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:3:59: error: root source file struct 'main' has no member named 'qux' #expect_error=main.zig:1:1: note: struct declared here @@ -56,7 +56,7 @@ pub fn main() !void { const foo = "good morning\n"; const bar = "good evening\n"; const qux = "good night\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good night\n" #update=remove unused declarations @@ -66,5 +66,5 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, @This().qux); } const qux = "good night\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="good night\n" diff --git a/test/incremental/bad_import b/test/incremental/bad_import index 20bdb9ae82fcb1588688d3ae9290bf146b1ee142..b9437714033b679afeb2fdea126c7f4d822f9177 100644 --- a/test/incremental/bad_import +++ b/test/incremental/bad_import @@ -11,7 +11,7 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "success\n"); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #file=foo.zig comptime { _ = @import("bad.zig"); @@ -34,5 +34,5 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "success\n"); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="success\n" diff --git a/test/incremental/change_embed_file b/test/incremental/change_embed_file index 84c9c334c72283d2ac5637064cd96a4fca37a0fe..92b9ec23e370e047973886cd613e8287adca5214 100644 --- a/test/incremental/change_embed_file +++ b/test/incremental/change_embed_file @@ -10,7 +10,7 @@ const string = @embedFile("string.txt"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, string); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #file=string.txt Hello, World! #expect_stdout="Hello, World!\n" @@ -31,7 +31,7 @@ const string = @embedFile("string.txt"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="a hardcoded string\n" #update=re-introduce reference to file @@ -41,7 +41,7 @@ const string = @embedFile("string.txt"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, string); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound #update=recreate file diff --git a/test/incremental/change_enum_tag_type b/test/incremental/change_enum_tag_type index 06b80ac04deb7f36956dff9d20c8704563602adc..97103351995157bd82b99b96913ffaa6165d2f96 100644 --- a/test/incremental/change_enum_tag_type +++ b/test/incremental/change_enum_tag_type @@ -19,7 +19,7 @@ pub fn main() !void { try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="a\n" #update=too many enum fields #file=main.zig @@ -43,7 +43,7 @@ comptime { std.debug.assert(@TypeOf(@intFromEnum(Foo.e)) == Tag); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:7:5: error: enumeration value '4' too large for type 'u2' #update=increase tag size #file=main.zig @@ -62,5 +62,5 @@ pub fn main() !void { try stdout_writer.interface.print("{s}\n", .{@tagName(val)}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="a\n" diff --git a/test/incremental/change_exports b/test/incremental/change_exports index a36afb4ee1aae151871068cae685a93df61111f6..05e3111cebe4903384284aaf0ee0daf9abc9c388 100644 --- a/test/incremental/change_exports +++ b/test/incremental/change_exports @@ -21,7 +21,7 @@ pub fn main() !void { try stdout_writer.interface.print("{}\n", .{S.bar}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="123\n" #update=add conflict @@ -44,7 +44,7 @@ pub fn main() !void { try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:6:5: error: exported symbol collision: foo #expect_error=main.zig:1:1: note: other symbol here @@ -68,7 +68,7 @@ pub fn main() !void { try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="123 456\n" #update=put exports in decl @@ -94,7 +94,7 @@ pub fn main() !void { try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="123 456\n" #update=remove reference to exporting decl @@ -141,7 +141,7 @@ pub fn main() !void { try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="123 456\n" #update=reintroduce reference to exporting decl, introducing conflict @@ -167,7 +167,7 @@ pub fn main() !void { try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other }); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:5:5: error: exported symbol collision: bar #expect_error=main.zig:2:1: note: other symbol here #expect_error=main.zig:6:5: error: exported symbol collision: other diff --git a/test/incremental/change_fn_type b/test/incremental/change_fn_type index b4286545e3816227d7d33354885875044c0b72fc..0c512e416e49b208939d07d1c8d80bf42f525afd 100644 --- a/test/incremental/change_fn_type +++ b/test/incremental/change_fn_type @@ -12,7 +12,7 @@ fn foo(x: u8) !void { return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="123\n" #update=change function type @@ -25,7 +25,7 @@ fn foo(x: i64) !void { return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="123\n" #update=change function argument @@ -38,5 +38,5 @@ fn foo(x: i64) !void { return stdout_writer.interface.print("{d}\n", .{x}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="-42\n" diff --git a/test/incremental/change_generic_line_number b/test/incremental/change_generic_line_number index 2d731b071c644c35bf921e20187820d81660b3b1..45b3d2f0d02ff724012328c84db643de027d11a6 100644 --- a/test/incremental/change_generic_line_number +++ b/test/incremental/change_generic_line_number @@ -4,7 +4,7 @@ #update=initial version #file=main.zig const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); fn Printer(message: []const u8) type { return struct { fn print() !void { @@ -20,7 +20,7 @@ pub fn main() !void { #update=change line number #file=main.zig const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); fn Printer(message: []const u8) type { return struct { diff --git a/test/incremental/change_line_number b/test/incremental/change_line_number index 7bafbbfbdd9d69538c094cbfbbfdaaf2b4f6a555..c95b690f7f07003c1fcafdc473334540dda5bd93 100644 --- a/test/incremental/change_line_number +++ b/test/incremental/change_line_number @@ -7,7 +7,7 @@ const std = @import("std"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "foo\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="foo\n" #update=change line number #file=main.zig @@ -16,5 +16,5 @@ const std = @import("std"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "foo\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="foo\n" diff --git a/test/incremental/change_panic_handler b/test/incremental/change_panic_handler index ebce3bc3121be81a04687dce2b77e7f27c39db4a..2d8e95f6e11b352b791ff66d927cc626f9bfc48b 100644 --- a/test/incremental/change_panic_handler +++ b/test/incremental/change_panic_handler @@ -17,7 +17,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { std.process.exit(0); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="panic message: integer overflow\n" #update=change the panic handler body @@ -35,7 +35,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { std.process.exit(0); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="new panic message: integer overflow\n" #update=change the panic handler function value @@ -53,5 +53,5 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn { std.process.exit(0); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="third panic message: integer overflow\n" diff --git a/test/incremental/change_panic_handler_explicit b/test/incremental/change_panic_handler_explicit index 366bffca45baab2478be62a00c90e780ef92597e..bf57a076282d7c609e307c333b5ccfb660b6c88e 100644 --- a/test/incremental/change_panic_handler_explicit +++ b/test/incremental/change_panic_handler_explicit @@ -47,7 +47,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { std.process.exit(0); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="panic message: integer overflow\n" #update=change the panic handler body @@ -95,7 +95,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { std.process.exit(0); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="new panic message: integer overflow\n" #update=change the panic handler function value @@ -143,5 +143,5 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn { std.process.exit(0); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="third panic message: integer overflow\n" diff --git a/test/incremental/change_shift_op b/test/incremental/change_shift_op index af849c0d5b6c28b3ee5a3d974ff9e74004f05ac0..159ef6f11d1ab0dbd6e7074cd49afa75b02af67e 100644 --- a/test/incremental/change_shift_op +++ b/test/incremental/change_shift_op @@ -13,7 +13,7 @@ fn foo(x: u16) !void { try stdout_writer.interface.print("0x{x}\n", .{x << 4}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="0x3000\n" #update=change to right shift #file=main.zig @@ -25,5 +25,5 @@ fn foo(x: u16) !void { try stdout_writer.interface.print("0x{x}\n", .{x >> 4}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="0x130\n" diff --git a/test/incremental/change_struct_same_fields b/test/incremental/change_struct_same_fields index 3ba715f906d087a34f5ce7f7aa5a81eb5012ce97..180948337020cba4922e030e2e01ef0a0c189a38 100644 --- a/test/incremental/change_struct_same_fields +++ b/test/incremental/change_struct_same_fields @@ -18,7 +18,7 @@ fn foo(val: *const S) !void { ); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="100 200\n" #update=change struct layout @@ -36,7 +36,7 @@ fn foo(val: *const S) !void { ); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="100 200\n" #update=change values @@ -54,5 +54,5 @@ fn foo(val: *const S) !void { ); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="1234 5678\n" diff --git a/test/incremental/change_zon_file b/test/incremental/change_zon_file index a966df5471c270edd0e67568e930e3a91687f839..beeffec4373c4ddf6ce48a7e566cd29f32d4e38f 100644 --- a/test/incremental/change_zon_file +++ b/test/incremental/change_zon_file @@ -10,7 +10,7 @@ const message: []const u8 = @import("message.zon"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, message); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #file=message.zon "Hello, World!\n" #expect_stdout="Hello, World!\n" @@ -32,7 +32,7 @@ const message: []const u8 = @import("message.zon"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=message.zon:1:1: error: unable to load 'message.zon': FileNotFound #expect_error=main.zig:2:37: note: file imported here @@ -48,5 +48,5 @@ const message: []const u8 = @import("message.zon"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, message); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="We're back, World!\n" diff --git a/test/incremental/change_zon_file_no_result_type b/test/incremental/change_zon_file_no_result_type index 6b3aa73dc6ee87c6e0e64fb2eaeb56508b8c10c7..d05aecedfbccf72fe96709b3aa42a02042b745a9 100644 --- a/test/incremental/change_zon_file_no_result_type +++ b/test/incremental/change_zon_file_no_result_type @@ -6,7 +6,7 @@ #update=initial version #file=main.zig const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, @import("foo.zon").message); } diff --git a/test/incremental/compile_log b/test/incremental/compile_log index 19ff7237f275a9cc946665ef1e8e08015d80bb56..3ed5467a9a175c559bef5b910215a05175fa8d5c 100644 --- a/test/incremental/compile_log +++ b/test/incremental/compile_log @@ -10,7 +10,7 @@ const std = @import("std"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, World!\n" #update=add compile log @@ -20,7 +20,7 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); @compileLog("this is a log"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:4:5: error: found compile log statement #expect_compile_log=@as(*const [13:0]u8, "this is a log") @@ -30,5 +30,5 @@ const std = @import("std"); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, World!\n" diff --git a/test/incremental/fix_astgen_failure b/test/incremental/fix_astgen_failure index dca371f521800d85e423cf68f008d78b598cfc97..701e9973a73e5df91b3d2cda86188c15b96ad67b 100644 --- a/test/incremental/fix_astgen_failure +++ b/test/incremental/fix_astgen_failure @@ -19,7 +19,7 @@ const std = @import("std"); pub fn hello() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, World!\n" #update=add new error #file=foo.zig @@ -27,7 +27,7 @@ const std = @import("std"); pub fn hello() !void { try std.Io.File.stdout().writeStreamingAll(io, hello_str); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=foo.zig:3:52: error: use of undeclared identifier 'hello_str' #update=fix the new error #file=foo.zig @@ -36,5 +36,5 @@ const hello_str = "Hello, World! Again!\n"; pub fn hello() !void { try std.Io.File.stdout().writeStreamingAll(io, hello_str); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, World! Again!\n" diff --git a/test/incremental/function_becomes_inline b/test/incremental/function_becomes_inline index 4021575842527a1c9705b411b634b354d44a6e04..eefb4f1a077d437389c79631f6bcd2953821e3f0 100644 --- a/test/incremental/function_becomes_inline +++ b/test/incremental/function_becomes_inline @@ -11,7 +11,7 @@ fn foo() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, World!\n" #update=make function inline @@ -23,7 +23,7 @@ inline fn foo() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, World!\n" #update=change string @@ -35,5 +35,5 @@ inline fn foo() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, `inline` World!\n"); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, `inline` World!\n" diff --git a/test/incremental/hello b/test/incremental/hello index 48659e1879ed97bad09ab32130401de4563c23cd..e2146e52be590779e8132e65ba9e5503a2d80e98 100644 --- a/test/incremental/hello +++ b/test/incremental/hello @@ -6,7 +6,7 @@ #update=initial version #file=main.zig const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "good morning\n"); } @@ -14,7 +14,7 @@ pub fn main() !void { #update=change the string #file=main.zig const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n"); } diff --git a/test/incremental/make_decl_pub b/test/incremental/make_decl_pub index b193deb68cf07f5c4709eb42da2a65d2997e4d92..a2c87b8f50114fa9b77216f0b4036e8a55e81fc7 100644 --- a/test/incremental/make_decl_pub +++ b/test/incremental/make_decl_pub @@ -14,7 +14,7 @@ const std = @import("std"); fn hello() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:3:12: error: 'hello' is not marked 'pub' #expect_error=foo.zig:2:1: note: declared here @@ -24,5 +24,5 @@ const std = @import("std"); pub fn hello() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, World!\n" diff --git a/test/incremental/modify_inline_fn b/test/incremental/modify_inline_fn index 19e201f1d9e2e994c8ff65c4fe780e96d8c818b0..6bf1f0baf29729ca12729955fe1683a6d3cf3706 100644 --- a/test/incremental/modify_inline_fn +++ b/test/incremental/modify_inline_fn @@ -13,7 +13,7 @@ pub fn main() !void { inline fn getStr() []const u8 { return "foo\n"; } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="foo\n" #update=change the string #file=main.zig @@ -25,5 +25,5 @@ pub fn main() !void { inline fn getStr() []const u8 { return "bar\n"; } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="bar\n" diff --git a/test/incremental/move_src b/test/incremental/move_src index b79a25df8df11002a6d4e7b1bf78c976ea760372..a60211e31b86f3dbf3746b28ee39df4c92e83803 100644 --- a/test/incremental/move_src +++ b/test/incremental/move_src @@ -16,7 +16,7 @@ fn foo() u32 { fn bar() u32 { return 123; } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="7 123\n" #update=add newline @@ -33,5 +33,5 @@ fn foo() u32 { fn bar() u32 { return 123; } -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="8 123\n" diff --git a/test/incremental/no_change_preserves_tag_names b/test/incremental/no_change_preserves_tag_names index dc89face50dbf5acbe8be6f50dddeedac323d3d7..06dc2f069c23634ad1d70ea30b997c555c483241 100644 --- a/test/incremental/no_change_preserves_tag_names +++ b/test/incremental/no_change_preserves_tag_names @@ -7,7 +7,7 @@ #file=main.zig const std = @import("std"); var some_enum: enum { first, second } = .first; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); } @@ -16,7 +16,7 @@ pub fn main() !void { #file=main.zig const std = @import("std"); var some_enum: enum { first, second } = .first; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); } diff --git a/test/incremental/recursive_function_becomes_non_recursive b/test/incremental/recursive_function_becomes_non_recursive index 5cee1bfbcff10ab669ee367e80a833d800df1cc6..ad8c0059fabba099e12f0e2b6055127aab0dc9a2 100644 --- a/test/incremental/recursive_function_becomes_non_recursive +++ b/test/incremental/recursive_function_becomes_non_recursive @@ -14,7 +14,7 @@ fn foo(recurse: bool) !void { try stdout.writeStreamingAll(io, "non-recursive path\n"); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="non-recursive path\n" #update=eliminate recursion and change argument @@ -28,5 +28,5 @@ fn foo(recurse: bool) !void { try stdout.writeStreamingAll(io, "non-recursive path\n"); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="x==1\n" diff --git a/test/incremental/remove_enum_field b/test/incremental/remove_enum_field index c964285707dec13202b9b917e390b6c90bf509ad..a1bbab8fd0fdaaf65115cb29d2b0210ccc77f862 100644 --- a/test/incremental/remove_enum_field +++ b/test/incremental/remove_enum_field @@ -14,7 +14,7 @@ pub fn main() !void { try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="1\n" #update=remove enum field #file=main.zig @@ -27,6 +27,6 @@ pub fn main() !void { try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)}); } const std = @import("std"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:7:69: error: enum 'main.MyEnum' has no member named 'foo' #expect_error=main.zig:1:16: note: enum declared here diff --git a/test/incremental/unreferenced_error b/test/incremental/unreferenced_error index c9a3277487c103a656a2b72fcb702fab27862856..25790ed0447950ebefa41f677d594e5dbc4b76f4 100644 --- a/test/incremental/unreferenced_error +++ b/test/incremental/unreferenced_error @@ -10,7 +10,7 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, a); } const a = "Hello, World!\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hello, World!\n" #update=introduce compile error @@ -20,7 +20,7 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, a); } const a = @compileError("bad a"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_error=main.zig:5:11: error: bad a #update=remove error reference @@ -31,7 +31,7 @@ pub fn main() !void { } const a = @compileError("bad a"); const b = "Hi there!\n"; -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Hi there!\n" #update=introduce and remove reference to error @@ -42,5 +42,5 @@ pub fn main() !void { } const a = "Back to a\n"; const b = @compileError("bad b"); -const io = std.Io.Threaded.global_single_threaded.ioBasic(); +const io = std.Io.Threaded.global_single_threaded.io(); #expect_stdout="Back to a\n" diff --git a/test/standalone/coff_dwarf/build.zig b/test/standalone/coff_dwarf/build.zig index d35e744a6c32d5c0b7a98f52c746506c2aefd2e2..0245154dbbe023a318fe3ba0d18f1c810b7958bf 100644 --- a/test/standalone/coff_dwarf/build.zig +++ b/test/standalone/coff_dwarf/build.zig @@ -46,6 +46,9 @@ pub fn build(b: *std.Build) void { lib.root_module.addCSourceFile(.{ .file = b.path("shared_lib.c"), .flags = &.{"-gdwarf"} }); exe.root_module.linkLibrary(lib); + if (target.result.os.tag == .windows) + exe.root_module.linkSystemLibrary("ws2_32", .{}); + const run = b.addRunArtifact(exe); run.expectExitCode(0); run.skip_foreign_checks = true; diff --git a/test/standalone/dirname/exists_in.zig b/test/standalone/dirname/exists_in.zig index 5900e76b574b1dd5d1628bc38e51133423b785cd..795b979e6f178a4cc65246d8ca0bb24480b1b075 100644 --- a/test/standalone/dirname/exists_in.zig +++ b/test/standalone/dirname/exists_in.zig @@ -26,7 +26,7 @@ pub fn main(init: std.process.Init) !void { return error.BadUsage; }; - const io = std.Io.Threaded.global_single_threaded.ioBasic(); + const io = std.Io.Threaded.global_single_threaded.io(); var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{}); defer dir.close(io); diff --git a/test/standalone/dirname/touch.zig b/test/standalone/dirname/touch.zig index 6b0dad536e4661244c70a5ecfb5c41035aec8f17..86aef059362b3e088f58a813a2e0de8043e6ac5e 100644 --- a/test/standalone/dirname/touch.zig +++ b/test/standalone/dirname/touch.zig @@ -21,7 +21,7 @@ pub fn main(init: std.process.Init) !void { const dir_path = std.Io.Dir.path.dirname(path) orelse unreachable; const basename = std.Io.Dir.path.basename(path); - const io = std.Io.Threaded.global_single_threaded.ioBasic(); + const io = std.Io.Threaded.global_single_threaded.io(); var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{}); defer dir.close(io); diff --git a/test/standalone/issue_5825/build.zig b/test/standalone/issue_5825/build.zig index e102febe2dcb702f7df29a6a0b95b8f36d2d096f..e4bc348d78f5701244ffbcf9bdfff24eefabd86e 100644 --- a/test/standalone/issue_5825/build.zig +++ b/test/standalone/issue_5825/build.zig @@ -34,6 +34,7 @@ pub fn build(b: *std.Build) void { exe.subsystem = .console; exe.root_module.linkSystemLibrary("kernel32", .{}); exe.root_module.linkSystemLibrary("ntdll", .{}); + exe.root_module.linkSystemLibrary("ws2_32", .{}); exe.root_module.addObject(obj); // TODO: actually check the output diff --git a/test/standalone/mix_o_files/build.zig b/test/standalone/mix_o_files/build.zig index 12b90f36247b379422b4ff616d671b85d5b5a66c..6ca7877058f2a5fb63e01eb796efd89b9a01f2bf 100644 --- a/test/standalone/mix_o_files/build.zig +++ b/test/standalone/mix_o_files/build.zig @@ -16,6 +16,9 @@ pub fn build(b: *std.Build) void { }), }); + if (target.result.os.tag == .windows) + obj.root_module.linkSystemLibrary("ws2_32", .{}); + const exe = b.addExecutable(.{ .name = "test", .root_module = b.createModule(.{ diff --git a/test/standalone/run_cwd/check_file_exists.zig b/test/standalone/run_cwd/check_file_exists.zig index bd4a26b6668cdd328b86ebe43fd56f280e59b0f7..2687cd8488a4bd2bb7c9b747b80e9cfad86c3b07 100644 --- a/test/standalone/run_cwd/check_file_exists.zig +++ b/test/standalone/run_cwd/check_file_exists.zig @@ -5,7 +5,7 @@ pub fn main(init: std.process.Init) !void { if (args.len != 2) return error.BadUsage; const path = args[1]; - const io = std.Io.Threaded.global_single_threaded.ioBasic(); + const io = std.Io.Threaded.global_single_threaded.io(); std.Io.Dir.cwd().access(io, path, .{}) catch return error.AccessFailed; } diff --git a/test/standalone/shared_library/build.zig b/test/standalone/shared_library/build.zig index 00c8e9cf34bd2951c814141103beef8d52b34f3c..3e9378eee02922d4024c3e5e6d2a31ea92b49842 100644 --- a/test/standalone/shared_library/build.zig +++ b/test/standalone/shared_library/build.zig @@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target = b.graph.host; + const target = b.standardTargetOptions(.{}); const exe_names: []const []const u8 = &.{ "test", "test-dync" }; const lib_names: []const []const u8 = &.{ "mathtest", "mathtest-dync" }; @@ -24,6 +24,9 @@ pub fn build(b: *std.Build) void { }), }); + if (target.result.os.tag == .windows) + lib.root_module.linkSystemLibrary("ws2_32", .{}); + const exe = b.addExecutable(.{ .name = exe_name, .root_module = b.createModule(.{ diff --git a/test/standalone/windows_argv/build.zig b/test/standalone/windows_argv/build.zig index 019cd34fc84c3f780d85ecbb41e10954bd929b5a..6d20b389c68b5e4bb74ce4efecaff66c272f2f52 100644 --- a/test/standalone/windows_argv/build.zig +++ b/test/standalone/windows_argv/build.zig @@ -20,6 +20,8 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }), }); + lib_gnu.root_module.linkSystemLibrary("ws2_32", .{}); + const verify_gnu = b.addExecutable(.{ .name = "verify-gnu", .root_module = b.createModule(.{ @@ -101,6 +103,7 @@ pub fn build(b: *std.Build) !void { .flags = &.{ "-DUNICODE", "-D_UNICODE" }, }); verify_msvc.root_module.linkLibrary(lib_msvc); + verify_msvc.root_module.linkSystemLibrary("ws2_32", .{}); verify_msvc.root_module.link_libc = true; const run_msvc = b.addRunArtifact(fuzz); -- 2.54.0