authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-09 09:09:04+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-09 09:09:04+01:00
log3b515fbede945a2927d5aba59212553a8b26b944
tree76f7583b67d1e817efb0d2f401836d9a229aaf83
parent6be202f46633d02e20d0f068a32296113ecb95ca
parent80625990d5ce82b781de54c4587b489cbd2cd55f

Merge pull request 'std.Io: move netReceive to become an Operation' (#31089) from net-receive into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31089

45 files changed, 517 insertions(+), 487 deletions(-)

lib/compiler/test_runner.zig+1-1
......@@ -17,7 +17,7 @@ var fba: std.heap.FixedBufferAllocator = .init(&fba_buffer);
1717var fba_buffer: [8192]u8 = undefined;
1818var stdin_buffer: [4096]u8 = undefined;
1919var stdout_buffer: [4096]u8 = undefined;
20const runner_threaded_io: Io = Io.Threaded.global_single_threaded.ioBasic();
20const runner_threaded_io: Io = Io.Threaded.global_single_threaded.io();
2121
2222/// Keep in sync with logic in `std.Build.addRunArtifact` which decides whether
2323/// the test runner will communicate with the build runner via `std.zig.Server`.
lib/compiler_rt.zig+1-1
......@@ -17,7 +17,7 @@ else
1717 null;
1818
1919pub const std_options_debug_io: std.Io = if (builtin.is_test)
20 std.Io.Threaded.global_single_threaded.ioBasic()
20 std.Io.Threaded.global_single_threaded.io()
2121else
2222 unreachable;
2323
lib/fuzzer.zig+1-1
......@@ -13,7 +13,7 @@ pub const std_options = std.Options{
1313 .logFn = logOverride,
1414};
1515
16const io = std.Io.Threaded.global_single_threaded.ioBasic();
16const io = std.Io.Threaded.global_single_threaded.io();
1717
1818fn logOverride(
1919 comptime level: std.log.Level,
lib/std/Io.zig+44-2
......@@ -243,7 +243,6 @@ pub const VTable = struct {
243243 netConnectUnix: *const fn (?*anyopaque, *const net.UnixAddress) net.UnixAddress.ConnectError!net.Socket.Handle,
244244 netSocketCreatePair: *const fn (?*anyopaque, net.Socket.CreatePairOptions) net.Socket.CreatePairError![2]net.Socket,
245245 netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) struct { ?net.Socket.SendError, usize },
246 netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize },
247246 /// Returns 0 on end of stream.
248247 netRead: *const fn (?*anyopaque, src: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize,
249248 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) {
261260 /// On Windows this is NtDeviceIoControlFile. On POSIX this is ioctl. On
262261 /// other systems this tag is unreachable.
263262 device_io_control: DeviceIoControl,
263 net_receive: NetReceive,
264264
265265 pub const Tag = @typeInfo(Operation).@"union".tag_type.?;
266266
......@@ -350,6 +350,35 @@ pub const Operation = union(enum) {
350350 },
351351 };
352352
353 pub const NetReceive = struct {
354 socket_handle: net.Socket.Handle,
355 message_buffer: []net.IncomingMessage,
356 data_buffer: []u8,
357 flags: net.ReceiveFlags,
358
359 pub const Error = error{
360 /// Insufficient memory or other resource internal to the operating system.
361 SystemResources,
362 /// Per-process limit on the number of open file descriptors has been reached.
363 ProcessFdQuotaExceeded,
364 /// System-wide limit on the total number of open files has been reached.
365 SystemFdQuotaExceeded,
366 /// Local end has been shut down on a connection-oriented socket, or
367 /// the socket was never connected.
368 SocketUnconnected,
369 /// The socket type requires that message be sent atomically, and the
370 /// size of the message to be sent made this impossible. The message
371 /// was not transmitted, or was partially transmitted.
372 MessageOversize,
373 /// Network connection was unexpectedly closed by sender.
374 ConnectionResetByPeer,
375 /// The local network interface used to reach the destination is offline.
376 NetworkDown,
377 } || Io.UnexpectedError;
378
379 pub const Result = struct { ?net.Socket.ReceiveError, usize };
380 };
381
353382 pub const Result = Result: {
354383 const operation_fields = @typeInfo(Operation).@"union".fields;
355384 var field_names: [operation_fields.len][]const u8 = undefined;
......@@ -417,6 +446,19 @@ pub fn operate(io: Io, operation: Operation) Cancelable!Operation.Result {
417446 return io.vtable.operate(io.userdata, operation);
418447}
419448
449pub const OperateTimeoutError = Cancelable || Timeout.Error || ConcurrentError;
450
451/// Performs one `Operation` with provided `timeout`.
452pub fn operateTimeout(io: Io, operation: Operation, timeout: Timeout) OperateTimeoutError!Operation.Result {
453 var storage: [1]Operation.Storage = undefined;
454 var batch: Batch = .init(&storage);
455 batch.addAt(0, operation);
456 try batch.awaitConcurrent(io, timeout);
457 const completion = batch.next().?;
458 assert(completion.index == 0);
459 return completion.result;
460}
461
420462/// Submits many operations together without waiting for all of them to
421463/// complete.
422464///
......@@ -1716,7 +1758,7 @@ pub const Event = enum(u32) {
17161758 }
17171759
17181760 /// Blocks until the logical boolean is `true`.
1719 pub fn wait(event: *Event, io: Io) Io.Cancelable!void {
1761 pub fn wait(event: *Event, io: Io) Cancelable!void {
17201762 if (@cmpxchgStrong(Event, event, .unset, .waiting, .acquire, .acquire)) |prev| switch (prev) {
17211763 .unset => unreachable,
17221764 .waiting => {},
lib/std/Io/Dispatch.zig+3-19
......@@ -459,7 +459,6 @@ pub fn io(ev: *Evented) Io {
459459 .netConnectUnix = netConnectUnixUnavailable,
460460 .netSocketCreatePair = netSocketCreatePairUnavailable,
461461 .netSend = netSendUnavailable,
462 .netReceive = netReceiveUnavailable,
463462 .netRead = netReadUnavailable,
464463 .netWrite = netWriteUnavailable,
465464 .netWriteFile = netWriteFileUnavailable,
......@@ -1714,6 +1713,7 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
17141713 },
17151714 },
17161715 .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) },
1716 .net_receive => @panic("TODO implement net_receive operation"),
17171717 }
17181718}
17191719
......@@ -2134,6 +2134,7 @@ fn batchDrainSubmitted(
21342134 break :result null;
21352135 },
21362136 .device_io_control => {},
2137 .net_receive => @panic("TODO implement batched net_receive"),
21372138 };
21382139 if (concurrency) return error.ConcurrencyUnavailable;
21392140 break :result try operate(ev, storage.submission.operation);
......@@ -2192,6 +2193,7 @@ fn batchSourceEvent(context: ?*anyopaque) callconv(.c) void {
21922193 } };
21932194 },
21942195 .device_io_control => unreachable,
2196 .net_receive => @panic("TODO implement batched net_receive"),
21952197 };
21962198
21972199 switch (pending.node.prev) {
......@@ -4872,24 +4874,6 @@ fn netSendUnavailable(
48724874 return .{ error.NetworkDown, 0 };
48734875}
48744876
4875fn netReceiveUnavailable(
4876 userdata: ?*anyopaque,
4877 handle: net.Socket.Handle,
4878 message_buffer: []net.IncomingMessage,
4879 data_buffer: []u8,
4880 flags: net.ReceiveFlags,
4881 timeout: Io.Timeout,
4882) struct { ?net.Socket.ReceiveTimeoutError, usize } {
4883 const ev: *Evented = @ptrCast(@alignCast(userdata));
4884 _ = ev;
4885 _ = handle;
4886 _ = message_buffer;
4887 _ = data_buffer;
4888 _ = flags;
4889 _ = timeout;
4890 return .{ error.NetworkDown, 0 };
4891}
4892
48934877fn netReadUnavailable(
48944878 userdata: ?*anyopaque,
48954879 fd: net.Socket.Handle,
lib/std/Io/Threaded.zig+309-295
......@@ -61,7 +61,7 @@ disable_memory_mapping: bool,
6161
6262stderr_writer: File.Writer = .{
6363 .io = undefined,
64 .interface = Io.File.Writer.initInterface(&.{}),
64 .interface = File.Writer.initInterface(&.{}),
6565 .file = if (is_windows) undefined else .stderr(),
6666 .mode = .streaming,
6767},
......@@ -160,7 +160,7 @@ pub const Environ = struct {
160160 },
161161 };
162162
163 pub fn scan(environ: *Environ, allocator: std.mem.Allocator) void {
163 pub fn scan(environ: *Environ, allocator: Allocator) void {
164164 if (is_windows) {
165165 // This value expires with any call that modifies the environment,
166166 // which is outside of this Io implementation's control, so references
......@@ -1901,10 +1901,6 @@ pub fn io(t: *Threaded) Io {
19011901 .windows => netSendWindows,
19021902 else => netSendPosix,
19031903 },
1904 .netReceive = switch (native_os) {
1905 .windows => netReceiveWindows,
1906 else => netReceivePosix,
1907 },
19081904 .netInterfaceNameResolve = netInterfaceNameResolve,
19091905 .netInterfaceName = netInterfaceName,
19101906 .netLookup = netLookup,
......@@ -1912,143 +1908,10 @@ pub fn io(t: *Threaded) Io {
19121908 };
19131909}
19141910
1915/// Same as `io` but disables all networking functionality, which has
1916/// an additional dependency on Windows (ws2_32).
1917pub fn ioBasic(t: *Threaded) Io {
1918 return .{
1919 .userdata = t,
1920 .vtable = &.{
1921 .crashHandler = crashHandler,
1922
1923 .async = async,
1924 .concurrent = concurrent,
1925 .await = await,
1926 .cancel = cancel,
1927
1928 .groupAsync = groupAsync,
1929 .groupConcurrent = groupConcurrent,
1930 .groupAwait = groupAwait,
1931 .groupCancel = groupCancel,
1932
1933 .recancel = recancel,
1934 .swapCancelProtection = swapCancelProtection,
1935 .checkCancel = checkCancel,
1936
1937 .futexWait = futexWait,
1938 .futexWaitUncancelable = futexWaitUncancelable,
1939 .futexWake = futexWake,
1940
1941 .operate = operate,
1942 .batchAwaitAsync = batchAwaitAsync,
1943 .batchAwaitConcurrent = batchAwaitConcurrent,
1944 .batchCancel = batchCancel,
1945
1946 .dirCreateDir = dirCreateDir,
1947 .dirCreateDirPath = dirCreateDirPath,
1948 .dirCreateDirPathOpen = dirCreateDirPathOpen,
1949 .dirStat = dirStat,
1950 .dirStatFile = dirStatFile,
1951 .dirAccess = dirAccess,
1952 .dirCreateFile = dirCreateFile,
1953 .dirCreateFileAtomic = dirCreateFileAtomic,
1954 .dirOpenFile = dirOpenFile,
1955 .dirOpenDir = dirOpenDir,
1956 .dirClose = dirClose,
1957 .dirRead = dirRead,
1958 .dirRealPath = dirRealPath,
1959 .dirRealPathFile = dirRealPathFile,
1960 .dirDeleteFile = dirDeleteFile,
1961 .dirDeleteDir = dirDeleteDir,
1962 .dirRename = dirRename,
1963 .dirRenamePreserve = dirRenamePreserve,
1964 .dirSymLink = dirSymLink,
1965 .dirReadLink = dirReadLink,
1966 .dirSetOwner = dirSetOwner,
1967 .dirSetFileOwner = dirSetFileOwner,
1968 .dirSetPermissions = dirSetPermissions,
1969 .dirSetFilePermissions = dirSetFilePermissions,
1970 .dirSetTimestamps = dirSetTimestamps,
1971 .dirHardLink = dirHardLink,
1972
1973 .fileStat = fileStat,
1974 .fileLength = fileLength,
1975 .fileClose = fileClose,
1976 .fileWritePositional = fileWritePositional,
1977 .fileWriteFileStreaming = fileWriteFileStreaming,
1978 .fileWriteFilePositional = fileWriteFilePositional,
1979 .fileReadPositional = fileReadPositional,
1980 .fileSeekBy = fileSeekBy,
1981 .fileSeekTo = fileSeekTo,
1982 .fileSync = fileSync,
1983 .fileIsTty = fileIsTty,
1984 .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,
1985 .fileSupportsAnsiEscapeCodes = fileSupportsAnsiEscapeCodes,
1986 .fileSetLength = fileSetLength,
1987 .fileSetOwner = fileSetOwner,
1988 .fileSetPermissions = fileSetPermissions,
1989 .fileSetTimestamps = fileSetTimestamps,
1990 .fileLock = fileLock,
1991 .fileTryLock = fileTryLock,
1992 .fileUnlock = fileUnlock,
1993 .fileDowngradeLock = fileDowngradeLock,
1994 .fileRealPath = fileRealPath,
1995 .fileHardLink = fileHardLink,
1996
1997 .fileMemoryMapCreate = fileMemoryMapCreate,
1998 .fileMemoryMapDestroy = fileMemoryMapDestroy,
1999 .fileMemoryMapSetLength = fileMemoryMapSetLength,
2000 .fileMemoryMapRead = fileMemoryMapRead,
2001 .fileMemoryMapWrite = fileMemoryMapWrite,
2002
2003 .processExecutableOpen = processExecutableOpen,
2004 .processExecutablePath = processExecutablePath,
2005 .lockStderr = lockStderr,
2006 .tryLockStderr = tryLockStderr,
2007 .unlockStderr = unlockStderr,
2008 .processCurrentPath = processCurrentPath,
2009 .processSetCurrentDir = processSetCurrentDir,
2010 .processSetCurrentPath = processSetCurrentPath,
2011 .processReplace = processReplace,
2012 .processReplacePath = processReplacePath,
2013 .processSpawn = processSpawn,
2014 .processSpawnPath = processSpawnPath,
2015 .childWait = childWait,
2016 .childKill = childKill,
2017
2018 .progressParentFile = progressParentFile,
2019
2020 .now = now,
2021 .clockResolution = clockResolution,
2022 .sleep = sleep,
2023
2024 .random = random,
2025 .randomSecure = randomSecure,
2026
2027 .netListenIp = netListenIpUnavailable,
2028 .netListenUnix = netListenUnixUnavailable,
2029 .netAccept = netAcceptUnavailable,
2030 .netBindIp = netBindIpUnavailable,
2031 .netConnectIp = netConnectIpUnavailable,
2032 .netSocketCreatePair = netSocketCreatePairUnavailable,
2033 .netConnectUnix = netConnectUnixUnavailable,
2034 .netClose = netCloseUnavailable,
2035 .netShutdown = netShutdownUnavailable,
2036 .netRead = netReadUnavailable,
2037 .netWrite = netWriteUnavailable,
2038 .netWriteFile = netWriteFileUnavailable,
2039 .netSend = netSendUnavailable,
2040 .netReceive = netReceiveUnavailable,
2041 .netInterfaceNameResolve = netInterfaceNameResolveUnavailable,
2042 .netInterfaceName = netInterfaceNameUnavailable,
2043 .netLookup = netLookupUnavailable,
2044 },
2045 };
2046}
2047
20481911pub const socket_flags_unsupported = is_darwin or native_os == .haiku;
20491912const have_accept4 = !socket_flags_unsupported;
20501913const have_flock_open_flags = @hasField(posix.O, "EXLOCK");
2051const have_networking = native_os != .wasi;
1914const have_networking = std.options.networking and native_os != .wasi;
20521915const have_flock = @TypeOf(posix.system.flock) != void;
20531916const have_sendmmsg = native_os == .linux;
20541917const have_futex = switch (builtin.cpu.arch) {
......@@ -2600,7 +2463,7 @@ fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io.
26002463 return;
26012464 }
26022465 const t: *Threaded = @ptrCast(@alignCast(userdata));
2603 const t_io = ioBasic(t);
2466 const t_io = io(t);
26042467 const timeout_ns: ?u64 = ns: {
26052468 const d = timeout.toDurationFromNow(t_io) orelse break :ns null;
26062469 break :ns std.math.lossyCast(u64, d.raw.toNanoseconds());
......@@ -2638,13 +2501,23 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
26382501 },
26392502 },
26402503 .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) },
2504 .net_receive => |*o| return .{ .net_receive = o: {
2505 if (!have_networking) break :o .{ error.NetworkDown, 0 };
2506 if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags);
2507 netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags, false) catch |err| switch (err) {
2508 error.Canceled => |e| return e,
2509 error.WouldBlock => unreachable,
2510 else => |e| break :o .{ e, 0 },
2511 };
2512 break :o .{ null, 1 };
2513 } },
26412514 }
26422515}
26432516
26442517fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
26452518 const t: *Threaded = @ptrCast(@alignCast(userdata));
26462519 if (is_windows) {
2647 batchDrainSubmittedWindows(b, false) catch |err| switch (err) {
2520 batchDrainSubmittedWindows(t, b, false) catch |err| switch (err) {
26482521 error.ConcurrencyUnavailable => unreachable, // passed concurrency=false
26492522 else => |e| return e,
26502523 };
......@@ -2662,11 +2535,19 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
26622535 const submission = &b.storage[index.toIndex()].submission;
26632536 switch (submission.operation) {
26642537 .file_read_streaming => |o| {
2665 poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.IN, .revents = 0 };
2538 poll_buffer[poll_len] = .{
2539 .fd = o.file.handle,
2540 .events = posix.POLL.IN | posix.POLL.ERR,
2541 .revents = 0,
2542 };
26662543 poll_len += 1;
26672544 },
26682545 .file_write_streaming => |o| {
2669 poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.OUT, .revents = 0 };
2546 poll_buffer[poll_len] = .{
2547 .fd = o.file.handle,
2548 .events = posix.POLL.OUT | posix.POLL.ERR,
2549 .revents = 0,
2550 };
26702551 poll_len += 1;
26712552 },
26722553 .device_io_control => |o| {
......@@ -2677,6 +2558,14 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
26772558 };
26782559 poll_len += 1;
26792560 },
2561 .net_receive => |*o| {
2562 poll_buffer[poll_len] = .{
2563 .fd = o.socket_handle,
2564 .events = posix.POLL.IN | posix.POLL.ERR,
2565 .revents = 0,
2566 };
2567 poll_len += 1;
2568 },
26802569 }
26812570 index = submission.node.next;
26822571 }
......@@ -2767,8 +2656,8 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
27672656fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.AwaitConcurrentError!void {
27682657 const t: *Threaded = @ptrCast(@alignCast(userdata));
27692658 if (is_windows) {
2770 const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t));
2771 try batchDrainSubmittedWindows(b, true);
2659 const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(io(t));
2660 try batchDrainSubmittedWindows(t, b, true);
27722661 while (b.pending.head != .none and b.completed.head == .none) {
27732662 var delay_interval: windows.LARGE_INTEGER = interval: {
27742663 const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER);
......@@ -2796,12 +2685,12 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
27962685 if (!have_poll) return error.ConcurrencyUnavailable;
27972686 var poll_buffer: [poll_buffer_len]posix.pollfd = undefined;
27982687 var poll_storage: struct {
2799 gpa: std.mem.Allocator,
2688 gpa: Allocator,
28002689 batch: *Io.Batch,
28012690 slice: []posix.pollfd,
28022691 len: u32,
28032692
2804 fn add(storage: *@This(), file: Io.File, events: @FieldType(posix.pollfd, "events")) Io.ConcurrentError!void {
2693 fn add(storage: *@This(), fd: File.Handle, events: @FieldType(posix.pollfd, "events")) Io.ConcurrentError!void {
28052694 const len = storage.len;
28062695 if (len == poll_buffer_len) {
28072696 const slice: []posix.pollfd = if (storage.batch.userdata) |batch_userdata|
......@@ -2816,7 +2705,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
28162705 storage.slice = slice;
28172706 }
28182707 storage.slice[len] = .{
2819 .fd = file.handle,
2708 .fd = fd,
28202709 .events = events,
28212710 .revents = 0,
28222711 };
......@@ -2826,18 +2715,41 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
28262715 {
28272716 var index = b.submitted.head;
28282717 while (index != .none) {
2829 const submission = &b.storage[index.toIndex()].submission;
2718 const storage = &b.storage[index.toIndex()];
2719 const submission = storage.submission;
28302720 switch (submission.operation) {
2831 .file_read_streaming => |o| try poll_storage.add(o.file, posix.POLL.IN),
2832 .file_write_streaming => |o| try poll_storage.add(o.file, posix.POLL.OUT),
2833 .device_io_control => |o| try poll_storage.add(o.file, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR),
2721 .file_read_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.ERR),
2722 .file_write_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.OUT | posix.POLL.ERR),
2723 .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR),
2724 .net_receive => |*o| nb: {
2725 var data_i: usize = 0;
2726 const result: Io.Operation.Result = .{ .net_receive = for (o.message_buffer, 0..) |*msg, msg_i| {
2727 const remaining_data_buffer = o.data_buffer[data_i..];
2728 netReceivePosix(o.socket_handle, msg, remaining_data_buffer, o.flags, true) catch |err| switch (err) {
2729 error.Canceled => |e| return e,
2730 error.WouldBlock => {
2731 if (msg_i != 0) break .{ null, msg_i };
2732 try poll_storage.add(o.socket_handle, posix.POLL.IN | posix.POLL.ERR);
2733 break :nb;
2734 },
2735 else => |e| break .{ e, 0 },
2736 };
2737 data_i += msg.data.len;
2738 } else .{ null, o.message_buffer.len } };
2739 switch (b.completed.tail) {
2740 .none => b.completed.head = index,
2741 else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
2742 }
2743 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
2744 b.completed.tail = index;
2745 },
28342746 }
28352747 index = submission.node.next;
28362748 }
28372749 }
28382750 switch (poll_storage.len) {
28392751 0 => return,
2840 1 => if (timeout == .none) {
2752 1 => if (timeout == .none and b.completed.head == .none) {
28412753 const index = b.submitted.head;
28422754 const storage = &b.storage[index.toIndex()];
28432755 const result = try operate(t, storage.submission.operation);
......@@ -2854,7 +2766,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
28542766 },
28552767 else => {},
28562768 }
2857 const t_io = ioBasic(t);
2769 const t_io = io(t);
28582770 const deadline = timeout.toTimestamp(t_io);
28592771 while (true) {
28602772 const timeout_ms: i32 = t: {
......@@ -2961,6 +2873,31 @@ fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void {
29612873 }
29622874}
29632875
2876fn batchCompleteBlockingWindows(
2877 b: *Io.Batch,
2878 operation_userdata: *WindowsBatchOperationUserdata,
2879 result: Io.Operation.Result,
2880) void {
2881 const erased_userdata = operation_userdata.toErased();
2882 const pending: *Io.Operation.Storage.Pending = @fieldParentPtr("userdata", erased_userdata);
2883 switch (pending.node.prev) {
2884 .none => b.pending.head = pending.node.next,
2885 else => |prev_index| b.storage[prev_index.toIndex()].pending.node.next = pending.node.next,
2886 }
2887 switch (pending.node.next) {
2888 .none => b.pending.tail = pending.node.prev,
2889 else => |next_index| b.storage[next_index.toIndex()].pending.node.prev = pending.node.prev,
2890 }
2891 const storage: *Io.Operation.Storage = @fieldParentPtr("pending", pending);
2892 const index: Io.Operation.OptionalIndex = .fromIndex(storage - b.storage.ptr);
2893 switch (b.completed.tail) {
2894 .none => b.completed.head = index,
2895 else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
2896 }
2897 b.completed.tail = index;
2898 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
2899}
2900
29642901fn batchApc(
29652902 apc_context: ?*anyopaque,
29662903 iosb: *windows.IO_STATUS_BLOCK,
......@@ -3000,6 +2937,7 @@ fn batchApc(
30002937 .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) },
30012938 .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) },
30022939 .device_io_control => .{ .device_io_control = iosb.* },
2940 .net_receive => unreachable,
30032941 };
30042942 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
30052943 },
......@@ -3007,7 +2945,7 @@ fn batchApc(
30072945}
30082946
30092947/// If `concurrency` is false, `error.ConcurrencyUnavailable` is unreachable.
3010fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentError || Io.Cancelable)!void {
2948fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io.ConcurrentError || Io.Cancelable)!void {
30112949 var index = b.submitted.head;
30122950 errdefer b.submitted.head = index;
30132951 while (index != .none) {
......@@ -3201,6 +3139,13 @@ fn batchDrainSubmittedWindows(b: *Io.Batch, concurrency: bool) (Io.ConcurrentErr
32013139 };
32023140 }
32033141 },
3142 .net_receive => |*o| {
3143 // TODO integrate with overlapped I/O or equivalent to avoid this error
3144 if (concurrency) return error.ConcurrencyUnavailable;
3145 batchCompleteBlockingWindows(b, operation_userdata, .{
3146 .net_receive = netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags),
3147 });
3148 },
32043149 }
32053150 index = submission.node.next;
32063151 }
......@@ -3459,7 +3404,7 @@ fn dirCreateDirPathOpenPosix(
34593404 options: Dir.OpenOptions,
34603405) Dir.CreateDirPathOpenError!Dir {
34613406 const t: *Threaded = @ptrCast(@alignCast(userdata));
3462 const t_io = ioBasic(t);
3407 const t_io = io(t);
34633408 return dirOpenDirPosix(t, dir, sub_path, options) catch |err| switch (err) {
34643409 error.FileNotFound => {
34653410 _ = try dir.createDirPathStatus(t_io, sub_path, permissions);
......@@ -3580,7 +3525,7 @@ fn dirCreateDirPathOpenWasi(
35803525 options: Dir.OpenOptions,
35813526) Dir.CreateDirPathOpenError!Dir {
35823527 const t: *Threaded = @ptrCast(@alignCast(userdata));
3583 const t_io = ioBasic(t);
3528 const t_io = io(t);
35843529 return dirOpenDirWasi(t, dir, sub_path, options) catch |err| switch (err) {
35853530 error.FileNotFound => {
35863531 _ = try dir.createDirPathStatus(t_io, sub_path, permissions);
......@@ -4621,7 +4566,7 @@ fn dirCreateFileAtomic(
46214566 options: Dir.CreateFileAtomicOptions,
46224567) Dir.CreateFileAtomicError!File.Atomic {
46234568 const t: *Threaded = @ptrCast(@alignCast(userdata));
4624 const t_io = ioBasic(t);
4569 const t_io = io(t);
46254570
46264571 // Linux has O_TMPFILE, but linkat() does not support AT_REPLACE, so it's
46274572 // useless when we have to make up a bogus path name to do the rename()
......@@ -10249,19 +10194,19 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
1024910194 const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &n);
1025010195 if (rc != 0) return error.NameTooLong;
1025110196 const symlink_path = std.mem.sliceTo(&symlink_path_buf, 0);
10252 return Io.Dir.realPathFileAbsolute(ioBasic(t), symlink_path, out_buffer) catch |err| switch (err) {
10197 return Io.Dir.realPathFileAbsolute(io(t), symlink_path, out_buffer) catch |err| switch (err) {
1025310198 error.NetworkNotFound => unreachable, // Windows-only
1025410199 error.FileBusy => unreachable, // Windows-only
1025510200 else => |e| return e,
1025610201 };
1025710202 },
10258 .linux, .serenity => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/exe", out_buffer) catch |err| switch (err) {
10203 .linux, .serenity => return Io.Dir.readLinkAbsolute(io(t), "/proc/self/exe", out_buffer) catch |err| switch (err) {
1025910204 error.UnsupportedReparsePointType => unreachable, // Windows-only
1026010205 error.NetworkNotFound => unreachable, // Windows-only
1026110206 error.FileBusy => unreachable, // Windows-only
1026210207 else => |e| return e,
1026310208 },
10264 .illumos => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
10209 .illumos => return Io.Dir.readLinkAbsolute(io(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
1026510210 error.UnsupportedReparsePointType => unreachable, // Windows-only
1026610211 error.NetworkNotFound => unreachable, // Windows-only
1026710212 error.FileBusy => unreachable, // Windows-only
......@@ -11623,7 +11568,7 @@ fn sleepPosix(timeout: Io.Timeout) Io.Cancelable!void {
1162311568}
1162411569
1162511570fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void {
11626 const t_io = ioBasic(t);
11571 const t_io = io(t);
1162711572 const w = std.os.wasi;
1162811573
1162911574 const clock: w.subscription_clock_t = if (timeout.toDurationFromNow(t_io)) |d| .{
......@@ -11652,7 +11597,7 @@ fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void {
1165211597}
1165311598
1165411599fn sleepNanosleep(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void {
11655 const t_io = ioBasic(t);
11600 const t_io = io(t);
1165611601 const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;
1165711602 const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;
1165811603
......@@ -11884,6 +11829,7 @@ fn netListenUnixWindows(
1188411829 options: net.UnixAddress.ListenOptions,
1188511830) net.UnixAddress.ListenError!net.Socket.Handle {
1188611831 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
11832 if (!have_networking) return error.NetworkDown;
1188711833 const t: *Threaded = @ptrCast(@alignCast(userdata));
1188811834
1188911835 const socket_handle = openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
......@@ -12380,6 +12326,7 @@ fn netConnectUnixWindows(
1238012326 address: *const net.UnixAddress,
1238112327) net.UnixAddress.ConnectError!net.Socket.Handle {
1238212328 if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
12329 if (!have_networking) return error.NetworkDown;
1238312330 const t: *Threaded = @ptrCast(@alignCast(userdata));
1238412331
1238512332 const socket_handle = try openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream });
......@@ -12995,11 +12942,76 @@ fn netSendWindows(
1299512942) struct { ?net.Socket.SendError, usize } {
1299612943 if (!have_networking) return .{ error.NetworkDown, 0 };
1299712944 const t: *Threaded = @ptrCast(@alignCast(userdata));
12998 _ = t;
12999 _ = handle;
13000 _ = messages;
13001 _ = flags;
13002 @panic("TODO netSendWindows");
12945
12946 // Ignored flags: confirm, eor, fastopen
12947 const windows_flags: u32 =
12948 @as(u32, if (flags.oob) ws2_32.MSG.OOB else 0) |
12949 @as(u32, if (flags.dont_route) ws2_32.MSG.DONTROUTE else 0);
12950
12951 for (messages, 0..) |*m, i| {
12952 netSendWindowsOne(t, handle, m, windows_flags) catch |err| return .{ err, i };
12953 }
12954 return .{ null, messages.len };
12955}
12956
12957fn netSendWindowsOne(
12958 t: *Threaded,
12959 handle: net.Socket.Handle,
12960 message: *net.OutgoingMessage,
12961 flags: u32,
12962) net.Socket.SendError!void {
12963 var buf: ws2_32.WSABUF = .{
12964 .buf = @constCast(message.data_ptr),
12965 .len = std.math.cast(u32, message.data_len) orelse return error.MessageOversize,
12966 };
12967 var n: u32 = undefined;
12968 var address: WsaAddress = undefined;
12969 const address_size = addressToWsa(message.address, &address);
12970 var syscall: Syscall = try .start();
12971 while (true) {
12972 const rc = ws2_32.WSASendTo(
12973 handle,
12974 (&buf)[0..1],
12975 1,
12976 &n,
12977 flags,
12978 &address.any,
12979 address_size,
12980 null,
12981 null,
12982 );
12983 if (rc != ws2_32.SOCKET_ERROR) {
12984 syscall.finish();
12985 return;
12986 }
12987 switch (ws2_32.WSAGetLastError()) {
12988 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
12989 try syscall.checkCancel();
12990 continue;
12991 },
12992 .NOTINITIALISED => {
12993 syscall.finish();
12994 try initializeWsa(t);
12995 syscall = try .start();
12996 continue;
12997 },
12998
12999 .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
13000 .ENETDOWN => return syscall.fail(error.NetworkDown),
13001 .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
13002 .ENOTCONN => return syscall.fail(error.SocketUnconnected),
13003 .EFAULT => unreachable, // a pointer is not completely contained in user address space.
13004
13005 else => |err| {
13006 syscall.finish();
13007 switch (err) {
13008 .EINVAL => return wsaErrorBug(err),
13009 .EMSGSIZE => return wsaErrorBug(err),
13010 else => return windows.unexpectedWSAError(err),
13011 }
13012 },
13013 }
13014 }
1300313015}
1300413016
1300513017fn netSendUnavailable(
......@@ -13190,70 +13202,44 @@ fn netSendMany(
1319013202}
1319113203
1319213204fn netReceivePosix(
13193 userdata: ?*anyopaque,
13194 handle: net.Socket.Handle,
13195 message_buffer: []net.IncomingMessage,
13205 socket_handle: net.Socket.Handle,
13206 message: *net.IncomingMessage,
1319613207 data_buffer: []u8,
1319713208 flags: net.ReceiveFlags,
13198 timeout: Io.Timeout,
13199) struct { ?net.Socket.ReceiveTimeoutError, usize } {
13200 if (!have_networking) return .{ error.NetworkDown, 0 };
13201 const t: *Threaded = @ptrCast(@alignCast(userdata));
13202 const t_io = io(t);
13203
13209 nonblocking: bool,
13210) (net.Socket.ReceiveError || error{WouldBlock})!void {
1320413211 // recvmmsg is useless, here's why:
1320513212 // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371)
1320613213 // * it wants iovecs for each message but we have a better API: one data
1320713214 // buffer to handle all the messages. The better API cannot be lowered to
1320813215 // the split vectors though because reducing the buffer size might make
1320913216 // some messages unreceivable.
13210
13211 // So the strategy instead is to use non-blocking recvmsg calls, calling
13212 // poll() with timeout if the first one returns EAGAIN.
1321313217 const posix_flags: u32 =
1321413218 @as(u32, if (flags.oob) posix.MSG.OOB else 0) |
1321513219 @as(u32, if (flags.peek) posix.MSG.PEEK else 0) |
1321613220 @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) |
13217 posix.MSG.DONTWAIT | posix.MSG.NOSIGNAL;
13221 posix.MSG.NOSIGNAL |
13222 @as(u32, if (nonblocking) posix.MSG.DONTWAIT else 0);
1321813223
13219 var poll_fds: [1]posix.pollfd = .{
13220 .{
13221 .fd = handle,
13222 .events = posix.POLL.IN,
13223 .revents = undefined,
13224 },
13224 var storage: PosixAddress = undefined;
13225 var iov: posix.iovec = .{ .base = data_buffer.ptr, .len = data_buffer.len };
13226 var msg: posix.msghdr = .{
13227 .name = &storage.any,
13228 .namelen = @sizeOf(PosixAddress),
13229 .iov = (&iov)[0..1],
13230 .iovlen = 1,
13231 .control = message.control.ptr,
13232 .controllen = @intCast(message.control.len),
13233 .flags = undefined,
1322513234 };
13226 var message_i: usize = 0;
13227 var data_i: usize = 0;
1322813235
13229 const deadline = timeout.toTimestamp(t_io);
13230
13231 recv: while (true) {
13232 if (message_buffer.len - message_i == 0) return .{ null, message_i };
13233 const message = &message_buffer[message_i];
13234 const remaining_data_buffer = data_buffer[data_i..];
13235 var storage: PosixAddress = undefined;
13236 var iov: posix.iovec = .{ .base = remaining_data_buffer.ptr, .len = remaining_data_buffer.len };
13237 var msg: posix.msghdr = .{
13238 .name = &storage.any,
13239 .namelen = @sizeOf(PosixAddress),
13240 .iov = (&iov)[0..1],
13241 .iovlen = 1,
13242 .control = message.control.ptr,
13243 .controllen = @intCast(message.control.len),
13244 .flags = undefined,
13245 };
13246
13247 const recv_rc = rc: {
13248 const syscall = Syscall.start() catch |err| return .{ err, message_i };
13249 const rc = posix.system.recvmsg(handle, &msg, posix_flags);
13250 syscall.finish();
13251 break :rc rc;
13252 };
13253 switch (posix.errno(recv_rc)) {
13236 const syscall = try Syscall.start();
13237 while (true) {
13238 const rc = posix.system.recvmsg(socket_handle, &msg, posix_flags);
13239 switch (posix.errno(rc)) {
1325413240 .SUCCESS => {
13255 const data = remaining_data_buffer[0..@intCast(recv_rc)];
13256 data_i += data.len;
13241 syscall.finish();
13242 const data = data_buffer[0..@intCast(rc)];
1325713243 message.* = .{
1325813244 .from = addressFromPosix(&storage),
1325913245 .data = data,
......@@ -13266,96 +13252,122 @@ fn netReceivePosix(
1326613252 .errqueue = if (@hasDecl(posix.MSG, "ERRQUEUE")) (msg.flags & posix.MSG.ERRQUEUE) != 0 else false,
1326713253 },
1326813254 };
13269 message_i += 1;
13270 continue;
13255 return;
1327113256 },
13272 .AGAIN => while (true) {
13273 if (message_i != 0) return .{ null, message_i };
13274
13275 const max_poll_ms = std.math.maxInt(u31);
13276 const timeout_ms: u31 = if (deadline) |d| t: {
13277 const duration = d.durationFromNow(t_io);
13278 if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i };
13279 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));
13280 } else max_poll_ms;
13281
13282 const syscall = Syscall.start() catch |err| return .{ err, message_i };
13283 const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms);
13284 syscall.finish();
13285
13286 switch (posix.errno(poll_rc)) {
13287 .SUCCESS => {
13288 if (poll_rc == 0) {
13289 // Although spurious timeouts are OK, when no deadline
13290 // is passed we must not return `error.Timeout`.
13291 if (deadline == null) continue;
13292 return .{ error.Timeout, message_i };
13293 }
13294 continue :recv;
13295 },
13296 .INTR => continue,
13297
13298 .FAULT => |err| return .{ errnoBug(err), message_i },
13299 .INVAL => |err| return .{ errnoBug(err), message_i },
13300 .NOMEM => return .{ error.SystemResources, message_i },
13301 else => |err| return .{ posix.unexpectedErrno(err), message_i },
13302 }
13257 .INTR => {
13258 try syscall.checkCancel();
13259 continue;
1330313260 },
13304 .INTR => continue,
13305
13306 .BADF => |err| return .{ errnoBug(err), message_i },
13307 .NFILE => return .{ error.SystemFdQuotaExceeded, message_i },
13308 .MFILE => return .{ error.ProcessFdQuotaExceeded, message_i },
13309 .FAULT => |err| return .{ errnoBug(err), message_i },
13310 .INVAL => |err| return .{ errnoBug(err), message_i },
13311 .NOBUFS => return .{ error.SystemResources, message_i },
13312 .NOMEM => return .{ error.SystemResources, message_i },
13313 .NOTCONN => return .{ error.SocketUnconnected, message_i },
13314 .NOTSOCK => |err| return .{ errnoBug(err), message_i },
13315 .MSGSIZE => return .{ error.MessageOversize, message_i },
13316 .PIPE => return .{ error.SocketUnconnected, message_i },
13317 .OPNOTSUPP => |err| return .{ errnoBug(err), message_i },
13318 .CONNRESET => return .{ error.ConnectionResetByPeer, message_i },
13319 .NETDOWN => return .{ error.NetworkDown, message_i },
13320 else => |err| return .{ posix.unexpectedErrno(err), message_i },
13261 .NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
13262 .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
13263 .NOBUFS => return syscall.fail(error.SystemResources),
13264 .NOMEM => return syscall.fail(error.SystemResources),
13265 .NOTCONN => return syscall.fail(error.SocketUnconnected),
13266 .MSGSIZE => return syscall.fail(error.MessageOversize),
13267 .PIPE => return syscall.fail(error.SocketUnconnected),
13268 .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
13269 .NETDOWN => return syscall.fail(error.NetworkDown),
13270 .AGAIN => return syscall.fail(error.WouldBlock),
13271 .BADF => |err| return syscall.errnoBug(err),
13272 .FAULT => |err| return syscall.errnoBug(err),
13273 .INVAL => |err| return syscall.errnoBug(err),
13274 .NOTSOCK => |err| return syscall.errnoBug(err),
13275 .OPNOTSUPP => |err| return syscall.errnoBug(err),
13276 else => |err| return syscall.unexpectedErrno(err),
1332113277 }
1332213278 }
1332313279}
1332413280
1332513281fn netReceiveWindows(
13326 userdata: ?*anyopaque,
13327 handle: net.Socket.Handle,
13282 t: *Threaded,
13283 socket_handle: net.Socket.Handle,
1332813284 message_buffer: []net.IncomingMessage,
1332913285 data_buffer: []u8,
1333013286 flags: net.ReceiveFlags,
13331 timeout: Io.Timeout,
13332) struct { ?net.Socket.ReceiveTimeoutError, usize } {
13333 if (!have_networking) return .{ error.NetworkDown, 0 };
13334 const t: *Threaded = @ptrCast(@alignCast(userdata));
13335 _ = t;
13336 _ = handle;
13337 _ = message_buffer;
13338 _ = data_buffer;
13339 _ = flags;
13340 _ = timeout;
13341 @panic("TODO implement netReceiveWindows");
13287) struct { ?net.Socket.ReceiveError, usize } {
13288 netReceiveWindowsOne(t, socket_handle, &message_buffer[0], data_buffer, flags) catch |err| return .{ err, 0 };
13289 return .{ null, 1 };
1334213290}
1334313291
13344fn netReceiveUnavailable(
13345 userdata: ?*anyopaque,
13346 handle: net.Socket.Handle,
13347 message_buffer: []net.IncomingMessage,
13292fn netReceiveWindowsOne(
13293 t: *Threaded,
13294 socket_handle: net.Socket.Handle,
13295 message: *net.IncomingMessage,
1334813296 data_buffer: []u8,
1334913297 flags: net.ReceiveFlags,
13350 timeout: Io.Timeout,
13351) struct { ?net.Socket.ReceiveTimeoutError, usize } {
13352 _ = userdata;
13353 _ = handle;
13354 _ = message_buffer;
13355 _ = data_buffer;
13356 _ = flags;
13357 _ = timeout;
13358 return .{ error.NetworkDown, 0 };
13298) net.Socket.ReceiveError!void {
13299 if (!have_networking) return error.NetworkDown;
13300
13301 var windows_flags: u32 =
13302 @as(u32, if (flags.oob) ws2_32.MSG.OOB else 0) |
13303 @as(u32, if (flags.peek) ws2_32.MSG.PEEK else 0) |
13304 @as(u32, if (flags.trunc) ws2_32.MSG.TRUNC else 0);
13305
13306 var buf: ws2_32.WSABUF = .{
13307 .buf = data_buffer.ptr,
13308 .len = std.math.cast(u32, data_buffer.len) orelse return error.MessageOversize,
13309 };
13310 var n: u32 = undefined;
13311 var syscall: Syscall = try .start();
13312 var from_storage: WsaAddress = undefined;
13313 var from_storage_len: i32 = @sizeOf(WsaAddress);
13314
13315 while (true) {
13316 const rc = ws2_32.WSARecvFrom(
13317 socket_handle,
13318 (&buf)[0..1],
13319 1,
13320 &n,
13321 &windows_flags,
13322 &from_storage.any,
13323 &from_storage_len,
13324 null,
13325 null,
13326 );
13327 if (rc != ws2_32.SOCKET_ERROR) {
13328 syscall.finish();
13329 message.* = .{
13330 .from = addressFromWsa(&from_storage),
13331 .data = data_buffer[0..n],
13332 .control = &.{},
13333 .flags = .{
13334 .eor = false,
13335 .trunc = (windows_flags & ws2_32.MSG.TRUNC) != 0,
13336 .ctrunc = (windows_flags & ws2_32.MSG.CTRUNC) != 0,
13337 .oob = false,
13338 .errqueue = false,
13339 },
13340 };
13341 return;
13342 }
13343 switch (ws2_32.WSAGetLastError()) {
13344 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
13345 try syscall.checkCancel();
13346 continue;
13347 },
13348 .NOTINITIALISED => {
13349 syscall.finish();
13350 try initializeWsa(t);
13351 syscall = try .start();
13352 continue;
13353 },
13354
13355 .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
13356 .ENETDOWN => return syscall.fail(error.NetworkDown),
13357 .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
13358 .ENOTCONN => return syscall.fail(error.SocketUnconnected),
13359 .EFAULT => unreachable, // a pointer is not completely contained in user address space.
13360
13361 else => |err| {
13362 syscall.finish();
13363 switch (err) {
13364 .EINVAL => return wsaErrorBug(err),
13365 .EMSGSIZE => return wsaErrorBug(err),
13366 else => return windows.unexpectedWSAError(err),
13367 }
13368 },
13369 }
13370 }
1335913371}
1336013372
1336113373fn netWritePosix(
......@@ -13459,6 +13471,7 @@ fn netWriteWindows(
1345913471 data: []const []const u8,
1346013472 splat: usize,
1346113473) net.Stream.Writer.Error!usize {
13474 if (!have_networking) return error.NetworkDown;
1346213475 const t: *Threaded = @ptrCast(@alignCast(userdata));
1346313476 comptime assert(is_windows);
1346413477
......@@ -13581,6 +13594,7 @@ fn addBuf(v: []posix.iovec_const, i: *iovlen_t, bytes: []const u8) void {
1358113594}
1358213595
1358313596fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
13597 if (!have_networking) unreachable;
1358413598 const t: *Threaded = @ptrCast(@alignCast(userdata));
1358513599 _ = t;
1358613600 switch (native_os) {
......@@ -13789,7 +13803,7 @@ fn netLookupUnavailable(
1378913803 _ = host_name;
1379013804 _ = options;
1379113805 const t: *Threaded = @ptrCast(@alignCast(userdata));
13792 resolved.close(ioBasic(t));
13806 resolved.close(io(t));
1379313807 return error.NetworkDown;
1379413808}
1379513809
......@@ -14072,7 +14086,7 @@ fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Can
1407214086
1407314087fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr {
1407414088 if (!t.stderr_writer_initialized) {
14075 const io_t = ioBasic(t);
14089 const io_t = io(t);
1407614090 if (is_windows) t.stderr_writer.file = .stderr();
1407714091 t.stderr_writer.io = io_t;
1407814092 t.stderr_writer_initialized = true;
lib/std/Io/Uring.zig+22-49
......@@ -777,7 +777,6 @@ pub fn io(ev: *Evented) Io {
777777 .netConnectUnix = netConnectUnixUnavailable,
778778 .netSocketCreatePair = netSocketCreatePairUnavailable,
779779 .netSend = netSendUnavailable,
780 .netReceive = netReceive,
781780 .netRead = netReadUnavailable,
782781 .netWrite = netWriteUnavailable,
783782 .netWriteFile = netWriteFileUnavailable,
......@@ -2092,6 +2091,18 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
20922091 .device_io_control => |o| .{
20932092 .device_io_control = try ev.deviceIoControl(try maybe_sync.enterSync(ev), o),
20942093 },
2094 .net_receive => |o| .{
2095 .net_receive = r: {
2096 const opt_err, const n = ev.netReceive(&maybe_sync.cancel_region, o.socket_handle, o.message_buffer, o.data_buffer, o.flags);
2097 break :r .{
2098 if (opt_err) |err| switch (err) {
2099 error.Canceled => |e| return e,
2100 else => |e| e,
2101 } else null,
2102 n,
2103 };
2104 },
2105 },
20952106 };
20962107}
20972108
......@@ -2375,6 +2386,10 @@ fn batchDrainSubmitted(
23752386 return error.ConcurrencyUnavailable
23762387 else
23772388 .{ .device_io_control = try ev.deviceIoControl(try maybe_sync.enterSync(ev), o) },
2389 .net_receive => |o| {
2390 _ = o;
2391 @panic("TODO implement batchDrainSubmitted for net_receive");
2392 },
23782393 })) |result| {
23792394 switch (batch.completed.tail) {
23802395 .none => batch.completed.head = index,
......@@ -2475,6 +2490,7 @@ fn batchDrainReady(batch: *Io.Batch) Io.Timeout.Error!void {
24752490 },
24762491 },
24772492 .device_io_control => unreachable,
2493 .net_receive => @panic("TODO"),
24782494 })) |result| {
24792495 switch (batch.completed.tail) {
24802496 .none => batch.completed.head = index,
......@@ -5035,37 +5051,16 @@ fn netSendUnavailable(
50355051}
50365052
50375053fn netReceive(
5038 userdata: ?*anyopaque,
5054 ev: *Evented,
5055 cancel_region: *CancelRegion,
50395056 handle: net.Socket.Handle,
50405057 message_buffer: []net.IncomingMessage,
50415058 data_buffer: []u8,
50425059 flags: net.ReceiveFlags,
5043 timeout: Io.Timeout,
5044) struct { ?net.Socket.ReceiveTimeoutError, usize } {
5045 const ev: *Evented = @ptrCast(@alignCast(userdata));
5046 const ev_io = ev.io();
5047
5060) struct { ?net.Socket.ReceiveError, usize } {
50485061 var message_i: usize = 0;
50495062 var data_i: usize = 0;
50505063
5051 const deadline: ?struct {
5052 raw: Io.Timestamp,
5053 timespec: linux.kernel_timespec,
5054 clock: Io.Clock,
5055 } = if (timeout.toTimestamp(ev_io)) |deadline| deadline: {
5056 const ns = deadline.raw.toNanoseconds();
5057 break :deadline .{
5058 .raw = deadline.raw,
5059 .timespec = .{
5060 .sec = @intCast(@divFloor(ns, std.time.ns_per_s)),
5061 .nsec = @intCast(@mod(ns, std.time.ns_per_s)),
5062 },
5063 .clock = deadline.clock,
5064 };
5065 } else null;
5066
5067 var cancel_region: CancelRegion = .init();
5068 defer cancel_region.deinit();
50695064 while (true) {
50705065 if (message_buffer.len - message_i == 0) return .{ null, message_i };
50715066 const message = &message_buffer[message_i];
......@@ -5085,7 +5080,7 @@ fn netReceive(
50855080 const thread = cancel_region.awaitIoUring() catch |err| return .{ err, message_i };
50865081 thread.enqueue().* = .{
50875082 .opcode = .RECVMSG,
5088 .flags = if (deadline) |_| linux.IOSQE_IO_LINK else 0,
5083 .flags = 0,
50895084 .ioprio = 0,
50905085 .fd = handle,
50915086 .off = 0,
......@@ -5102,26 +5097,6 @@ fn netReceive(
51025097 .addr3 = 0,
51035098 .resv = 0,
51045099 };
5105 if (deadline) |*deadline_ptr| thread.enqueue().* = .{
5106 .opcode = .LINK_TIMEOUT,
5107 .flags = linux.IOSQE_CQE_SKIP_SUCCESS,
5108 .ioprio = 0,
5109 .fd = 0,
5110 .off = 0,
5111 .addr = @intFromPtr(&deadline_ptr.timespec),
5112 .len = 1,
5113 .rw_flags = linux.IORING_TIMEOUT_ABS | @as(u32, switch (deadline_ptr.clock) {
5114 .real => linux.IORING_TIMEOUT_REALTIME,
5115 else => 0,
5116 .boot => linux.IORING_TIMEOUT_BOOTTIME,
5117 }),
5118 .user_data = @intFromEnum(Completion.Userdata.wakeup),
5119 .buf_index = 0,
5120 .personality = 0,
5121 .splice_fd_in = 0,
5122 .addr3 = 0,
5123 .resv = 0,
5124 };
51255100 ev.yield(null, .nothing);
51265101 const completion = cancel_region.completion();
51275102 switch (completion.errno()) {
......@@ -5144,9 +5119,7 @@ fn netReceive(
51445119 continue;
51455120 },
51465121 .AGAIN => unreachable,
5147 .INTR, .CANCELED => if (deadline) |d| if (now(ev, d.clock).nanoseconds >= d.raw.nanoseconds)
5148 return .{ error.Timeout, message_i },
5149
5122 .INTR, .CANCELED => {},
51505123 .BADF => |err| return .{ errnoBug(err), message_i },
51515124 .NFILE => return .{ error.SystemFdQuotaExceeded, message_i },
51525125 .MFILE => return .{ error.ProcessFdQuotaExceeded, message_i },
lib/std/Io/net.zig+22-28
......@@ -1109,25 +1109,7 @@ pub const Socket = struct {
11091109 if (n != messages.len) return err.?;
11101110 }
11111111
1112 pub const ReceiveError = error{
1113 /// Insufficient memory or other resource internal to the operating system.
1114 SystemResources,
1115 /// Per-process limit on the number of open file descriptors has been reached.
1116 ProcessFdQuotaExceeded,
1117 /// System-wide limit on the total number of open files has been reached.
1118 SystemFdQuotaExceeded,
1119 /// Local end has been shut down on a connection-oriented socket, or
1120 /// the socket was never connected.
1121 SocketUnconnected,
1122 /// The socket type requires that message be sent atomically, and the
1123 /// size of the message to be sent made this impossible. The message
1124 /// was not transmitted, or was partially transmitted.
1125 MessageOversize,
1126 /// Network connection was unexpectedly closed by sender.
1127 ConnectionResetByPeer,
1128 /// The local network interface used to reach the destination is offline.
1129 NetworkDown,
1130 } || Io.UnexpectedError || Io.Cancelable;
1112 pub const ReceiveError = Io.Operation.NetReceive.Error || Io.Cancelable;
11311113
11321114 /// Waits for data. Connectionless.
11331115 ///
......@@ -1135,17 +1117,18 @@ pub const Socket = struct {
11351117 /// * `receiveTimeout`
11361118 pub fn receive(s: *const Socket, io: Io, buffer: []u8) ReceiveError!IncomingMessage {
11371119 var message: IncomingMessage = .init;
1138 const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, .none);
1139 if (maybe_err) |err| switch (err) {
1140 // No timeout is passed to `netReceieve`, so it must not return timeout related errors.
1141 error.Timeout => unreachable,
1142 else => |e| return e,
1143 };
1120 const maybe_err, const count = (try io.operate(.{ .net_receive = .{
1121 .socket_handle = s.handle,
1122 .message_buffer = (&message)[0..1],
1123 .data_buffer = buffer,
1124 .flags = .{},
1125 } })).net_receive;
1126 if (maybe_err) |err| return err;
11441127 assert(1 == count);
11451128 return message;
11461129 }
11471130
1148 pub const ReceiveTimeoutError = ReceiveError || Io.Timeout.Error;
1131 pub const ReceiveTimeoutError = ReceiveError || Io.Timeout.Error || Io.ConcurrentError;
11491132
11501133 /// Waits for data. Connectionless.
11511134 ///
......@@ -1161,7 +1144,12 @@ pub const Socket = struct {
11611144 timeout: Io.Timeout,
11621145 ) ReceiveTimeoutError!IncomingMessage {
11631146 var message: IncomingMessage = .init;
1164 const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, timeout);
1147 const maybe_err, const count = (try io.operateTimeout(.{ .net_receive = .{
1148 .socket_handle = s.handle,
1149 .message_buffer = (&message)[0..1],
1150 .data_buffer = buffer,
1151 .flags = .{},
1152 } }, timeout)).net_receive;
11651153 if (maybe_err) |err| return err;
11661154 assert(1 == count);
11671155 return message;
......@@ -1186,7 +1174,13 @@ pub const Socket = struct {
11861174 flags: ReceiveFlags,
11871175 timeout: Io.Timeout,
11881176 ) struct { ?ReceiveTimeoutError, usize } {
1189 return io.vtable.netReceive(io.userdata, s.handle, message_buffer, data_buffer, flags, timeout);
1177 const result = io.operateTimeout(.{ .net_receive = .{
1178 .socket_handle = s.handle,
1179 .message_buffer = message_buffer,
1180 .data_buffer = data_buffer,
1181 .flags = flags,
1182 } }, timeout) catch |err| return .{ err, 0 };
1183 return result.net_receive;
11901184 }
11911185
11921186 pub const CreatePairError = error{
lib/std/os/windows/ws2_32.zig+14-11
......@@ -661,17 +661,20 @@ pub const IOC_OUT = 1073741824;
661661pub const IOC_IN = 2147483648;
662662
663663pub const MSG = struct {
664 pub const TRUNC = 256;
665 pub const CTRUNC = 512;
666 pub const BCAST = 1024;
667 pub const MCAST = 2048;
668 pub const ERRQUEUE = 4096;
669
670 pub const PEEK = 2;
671 pub const WAITALL = 8;
672 pub const PUSH_IMMEDIATE = 32;
673 pub const PARTIAL = 32768;
674 pub const INTERRUPT = 16;
664 pub const OOB = 0x1;
665 pub const PEEK = 0x2;
666 pub const DONTROUTE = 0x4;
667 pub const WAITALL = 0x8;
668 pub const INTERRUPT = 0x10;
669 pub const PUSH_IMMEDIATE = 0x20;
670
671 pub const TRUNC = 0x0100;
672 pub const CTRUNC = 0x0200;
673 pub const BCAST = 0x0400;
674 pub const MCAST = 0x0800;
675
676 pub const PARTIAL = 0x8000;
677
675678 pub const MAXIOVLEN = 16;
676679};
677680
lib/std/std.zig+4-1
......@@ -174,6 +174,9 @@ pub const Options = struct {
174174 /// stack traces will just print an error to the relevant `Io.Writer` and return.
175175 allow_stack_tracing: bool = !@import("builtin").strip_debug_info,
176176
177 /// Allows disabling networking in std.Io implementations.
178 networking: bool = true,
179
177180 /// TODO This is a separate decl instead of a field as a workaround around
178181 /// compilation errors due to zig not being lazy enough.
179182 pub const logTerminalMode: fn () Io.Terminal.Mode = log.defaultTerminalMode;
......@@ -202,7 +205,7 @@ pub const Options = struct {
202205 /// implementation based on coroutines, one likely wants `std.debug.print`
203206 /// to directly write to stderr without trying to interact with the code
204207 /// being debugged.
205 pub const debug_io: Io = if (@hasDecl(root, "std_options_debug_io")) root.std_options_debug_io else debug_threaded_io.?.ioBasic();
208 pub const debug_io: Io = if (@hasDecl(root, "std_options_debug_io")) root.std_options_debug_io else debug_threaded_io.?.io();
206209
207210 /// Overrides `std.Io.File.Permissions`.
208211 pub const FilePermissions: ?type = if (@hasDecl(root, "std_options_FilePermissions")) root.std_options_FilePermissions else null;
lib/ubsan_rt.zig+4
......@@ -3,6 +3,10 @@ const builtin = @import("builtin");
33const assert = std.debug.assert;
44const panic = std.debug.panicExtra;
55
6pub const std_options: std.Options = .{
7 .networking = false,
8};
9
610const SourceLocation = extern struct {
711 file_name: ?[*:0]const u8,
812 line: u32,
test/incremental/add_decl+6-6
......@@ -10,7 +10,7 @@ pub fn main() !void {
1010 try std.Io.File.stdout().writeStreamingAll(io, foo);
1111}
1212const foo = "good morning\n";
13const io = std.Io.Threaded.global_single_threaded.ioBasic();
13const io = std.Io.Threaded.global_single_threaded.io();
1414#expect_stdout="good morning\n"
1515
1616#update=add new declaration
......@@ -21,7 +21,7 @@ pub fn main() !void {
2121}
2222const foo = "good morning\n";
2323const bar = "good evening\n";
24const io = std.Io.Threaded.global_single_threaded.ioBasic();
24const io = std.Io.Threaded.global_single_threaded.io();
2525#expect_stdout="good morning\n"
2626
2727#update=reference new declaration
......@@ -32,7 +32,7 @@ pub fn main() !void {
3232}
3333const foo = "good morning\n";
3434const bar = "good evening\n";
35const io = std.Io.Threaded.global_single_threaded.ioBasic();
35const io = std.Io.Threaded.global_single_threaded.io();
3636#expect_stdout="good evening\n"
3737
3838#update=reference missing declaration
......@@ -43,7 +43,7 @@ pub fn main() !void {
4343}
4444const foo = "good morning\n";
4545const bar = "good evening\n";
46const io = std.Io.Threaded.global_single_threaded.ioBasic();
46const io = std.Io.Threaded.global_single_threaded.io();
4747#expect_error=main.zig:3:52: error: use of undeclared identifier 'qux'
4848
4949#update=add missing declaration
......@@ -55,7 +55,7 @@ pub fn main() !void {
5555const foo = "good morning\n";
5656const bar = "good evening\n";
5757const qux = "good night\n";
58const io = std.Io.Threaded.global_single_threaded.ioBasic();
58const io = std.Io.Threaded.global_single_threaded.io();
5959#expect_stdout="good night\n"
6060
6161#update=remove unused declarations
......@@ -65,5 +65,5 @@ pub fn main() !void {
6565 try std.Io.File.stdout().writeStreamingAll(io, qux);
6666}
6767const qux = "good night\n";
68const io = std.Io.Threaded.global_single_threaded.ioBasic();
68const io = std.Io.Threaded.global_single_threaded.io();
6969#expect_stdout="good night\n"
test/incremental/add_decl_namespaced+6-6
......@@ -10,7 +10,7 @@ pub fn main() !void {
1010 try std.Io.File.stdout().writeStreamingAll(io, @This().foo);
1111}
1212const foo = "good morning\n";
13const io = std.Io.Threaded.global_single_threaded.ioBasic();
13const io = std.Io.Threaded.global_single_threaded.io();
1414#expect_stdout="good morning\n"
1515
1616#update=add new declaration
......@@ -21,7 +21,7 @@ pub fn main() !void {
2121}
2222const foo = "good morning\n";
2323const bar = "good evening\n";
24const io = std.Io.Threaded.global_single_threaded.ioBasic();
24const io = std.Io.Threaded.global_single_threaded.io();
2525#expect_stdout="good morning\n"
2626
2727#update=reference new declaration
......@@ -32,7 +32,7 @@ pub fn main() !void {
3232}
3333const foo = "good morning\n";
3434const bar = "good evening\n";
35const io = std.Io.Threaded.global_single_threaded.ioBasic();
35const io = std.Io.Threaded.global_single_threaded.io();
3636#expect_stdout="good evening\n"
3737
3838#update=reference missing declaration
......@@ -43,7 +43,7 @@ pub fn main() !void {
4343}
4444const foo = "good morning\n";
4545const bar = "good evening\n";
46const io = std.Io.Threaded.global_single_threaded.ioBasic();
46const io = std.Io.Threaded.global_single_threaded.io();
4747#expect_error=main.zig:3:59: error: root source file struct 'main' has no member named 'qux'
4848#expect_error=main.zig:1:1: note: struct declared here
4949
......@@ -56,7 +56,7 @@ pub fn main() !void {
5656const foo = "good morning\n";
5757const bar = "good evening\n";
5858const qux = "good night\n";
59const io = std.Io.Threaded.global_single_threaded.ioBasic();
59const io = std.Io.Threaded.global_single_threaded.io();
6060#expect_stdout="good night\n"
6161
6262#update=remove unused declarations
......@@ -66,5 +66,5 @@ pub fn main() !void {
6666 try std.Io.File.stdout().writeStreamingAll(io, @This().qux);
6767}
6868const qux = "good night\n";
69const io = std.Io.Threaded.global_single_threaded.ioBasic();
69const io = std.Io.Threaded.global_single_threaded.io();
7070#expect_stdout="good night\n"
test/incremental/bad_import+2-2
......@@ -11,7 +11,7 @@ pub fn main() !void {
1111 try std.Io.File.stdout().writeStreamingAll(io, "success\n");
1212}
1313const std = @import("std");
14const io = std.Io.Threaded.global_single_threaded.ioBasic();
14const io = std.Io.Threaded.global_single_threaded.io();
1515#file=foo.zig
1616comptime {
1717 _ = @import("bad.zig");
......@@ -34,5 +34,5 @@ pub fn main() !void {
3434 try std.Io.File.stdout().writeStreamingAll(io, "success\n");
3535}
3636const std = @import("std");
37const io = std.Io.Threaded.global_single_threaded.ioBasic();
37const io = std.Io.Threaded.global_single_threaded.io();
3838#expect_stdout="success\n"
test/incremental/change_embed_file+3-3
......@@ -10,7 +10,7 @@ const string = @embedFile("string.txt");
1010pub fn main() !void {
1111 try std.Io.File.stdout().writeStreamingAll(io, string);
1212}
13const io = std.Io.Threaded.global_single_threaded.ioBasic();
13const io = std.Io.Threaded.global_single_threaded.io();
1414#file=string.txt
1515Hello, World!
1616#expect_stdout="Hello, World!\n"
......@@ -31,7 +31,7 @@ const string = @embedFile("string.txt");
3131pub fn main() !void {
3232 try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n");
3333}
34const io = std.Io.Threaded.global_single_threaded.ioBasic();
34const io = std.Io.Threaded.global_single_threaded.io();
3535#expect_stdout="a hardcoded string\n"
3636
3737#update=re-introduce reference to file
......@@ -41,7 +41,7 @@ const string = @embedFile("string.txt");
4141pub fn main() !void {
4242 try std.Io.File.stdout().writeStreamingAll(io, string);
4343}
44const io = std.Io.Threaded.global_single_threaded.ioBasic();
44const io = std.Io.Threaded.global_single_threaded.io();
4545#expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound
4646
4747#update=recreate file
test/incremental/change_enum_tag_type+3-3
......@@ -19,7 +19,7 @@ pub fn main() !void {
1919 try stdout_writer.interface.print("{s}\n", .{@tagName(val)});
2020}
2121const std = @import("std");
22const io = std.Io.Threaded.global_single_threaded.ioBasic();
22const io = std.Io.Threaded.global_single_threaded.io();
2323#expect_stdout="a\n"
2424#update=too many enum fields
2525#file=main.zig
......@@ -43,7 +43,7 @@ comptime {
4343 std.debug.assert(@TypeOf(@intFromEnum(Foo.e)) == Tag);
4444}
4545const std = @import("std");
46const io = std.Io.Threaded.global_single_threaded.ioBasic();
46const io = std.Io.Threaded.global_single_threaded.io();
4747#expect_error=main.zig:7:5: error: enumeration value '4' too large for type 'u2'
4848#update=increase tag size
4949#file=main.zig
......@@ -62,5 +62,5 @@ pub fn main() !void {
6262 try stdout_writer.interface.print("{s}\n", .{@tagName(val)});
6363}
6464const std = @import("std");
65const io = std.Io.Threaded.global_single_threaded.ioBasic();
65const io = std.Io.Threaded.global_single_threaded.io();
6666#expect_stdout="a\n"
test/incremental/change_exports+6-6
......@@ -21,7 +21,7 @@ pub fn main() !void {
2121 try stdout_writer.interface.print("{}\n", .{S.bar});
2222}
2323const std = @import("std");
24const io = std.Io.Threaded.global_single_threaded.ioBasic();
24const io = std.Io.Threaded.global_single_threaded.io();
2525#expect_stdout="123\n"
2626
2727#update=add conflict
......@@ -44,7 +44,7 @@ pub fn main() !void {
4444 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
4545}
4646const std = @import("std");
47const io = std.Io.Threaded.global_single_threaded.ioBasic();
47const io = std.Io.Threaded.global_single_threaded.io();
4848#expect_error=main.zig:6:5: error: exported symbol collision: foo
4949#expect_error=main.zig:1:1: note: other symbol here
5050
......@@ -68,7 +68,7 @@ pub fn main() !void {
6868 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
6969}
7070const std = @import("std");
71const io = std.Io.Threaded.global_single_threaded.ioBasic();
71const io = std.Io.Threaded.global_single_threaded.io();
7272#expect_stdout="123 456\n"
7373
7474#update=put exports in decl
......@@ -94,7 +94,7 @@ pub fn main() !void {
9494 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
9595}
9696const std = @import("std");
97const io = std.Io.Threaded.global_single_threaded.ioBasic();
97const io = std.Io.Threaded.global_single_threaded.io();
9898#expect_stdout="123 456\n"
9999
100100#update=remove reference to exporting decl
......@@ -141,7 +141,7 @@ pub fn main() !void {
141141 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
142142}
143143const std = @import("std");
144const io = std.Io.Threaded.global_single_threaded.ioBasic();
144const io = std.Io.Threaded.global_single_threaded.io();
145145#expect_stdout="123 456\n"
146146
147147#update=reintroduce reference to exporting decl, introducing conflict
......@@ -167,7 +167,7 @@ pub fn main() !void {
167167 try stdout_writer.interface.print("{} {}\n", .{ S.bar, S.other });
168168}
169169const std = @import("std");
170const io = std.Io.Threaded.global_single_threaded.ioBasic();
170const io = std.Io.Threaded.global_single_threaded.io();
171171#expect_error=main.zig:5:5: error: exported symbol collision: bar
172172#expect_error=main.zig:2:1: note: other symbol here
173173#expect_error=main.zig:6:5: error: exported symbol collision: other
test/incremental/change_fn_type+3-3
......@@ -12,7 +12,7 @@ fn foo(x: u8) !void {
1212 return stdout_writer.interface.print("{d}\n", .{x});
1313}
1414const std = @import("std");
15const io = std.Io.Threaded.global_single_threaded.ioBasic();
15const io = std.Io.Threaded.global_single_threaded.io();
1616#expect_stdout="123\n"
1717
1818#update=change function type
......@@ -25,7 +25,7 @@ fn foo(x: i64) !void {
2525 return stdout_writer.interface.print("{d}\n", .{x});
2626}
2727const std = @import("std");
28const io = std.Io.Threaded.global_single_threaded.ioBasic();
28const io = std.Io.Threaded.global_single_threaded.io();
2929#expect_stdout="123\n"
3030
3131#update=change function argument
......@@ -38,5 +38,5 @@ fn foo(x: i64) !void {
3838 return stdout_writer.interface.print("{d}\n", .{x});
3939}
4040const std = @import("std");
41const io = std.Io.Threaded.global_single_threaded.ioBasic();
41const io = std.Io.Threaded.global_single_threaded.io();
4242#expect_stdout="-42\n"
test/incremental/change_generic_line_number+2-2
......@@ -4,7 +4,7 @@
44#update=initial version
55#file=main.zig
66const std = @import("std");
7const io = std.Io.Threaded.global_single_threaded.ioBasic();
7const io = std.Io.Threaded.global_single_threaded.io();
88fn Printer(message: []const u8) type {
99 return struct {
1010 fn print() !void {
......@@ -20,7 +20,7 @@ pub fn main() !void {
2020#update=change line number
2121#file=main.zig
2222const std = @import("std");
23const io = std.Io.Threaded.global_single_threaded.ioBasic();
23const io = std.Io.Threaded.global_single_threaded.io();
2424
2525fn Printer(message: []const u8) type {
2626 return struct {
test/incremental/change_line_number+2-2
......@@ -7,7 +7,7 @@ const std = @import("std");
77pub fn main() !void {
88 try std.Io.File.stdout().writeStreamingAll(io, "foo\n");
99}
10const io = std.Io.Threaded.global_single_threaded.ioBasic();
10const io = std.Io.Threaded.global_single_threaded.io();
1111#expect_stdout="foo\n"
1212#update=change line number
1313#file=main.zig
......@@ -16,5 +16,5 @@ const std = @import("std");
1616pub fn main() !void {
1717 try std.Io.File.stdout().writeStreamingAll(io, "foo\n");
1818}
19const io = std.Io.Threaded.global_single_threaded.ioBasic();
19const io = std.Io.Threaded.global_single_threaded.io();
2020#expect_stdout="foo\n"
test/incremental/change_panic_handler+3-3
......@@ -17,7 +17,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn {
1717 std.process.exit(0);
1818}
1919const std = @import("std");
20const io = std.Io.Threaded.global_single_threaded.ioBasic();
20const io = std.Io.Threaded.global_single_threaded.io();
2121#expect_stdout="panic message: integer overflow\n"
2222
2323#update=change the panic handler body
......@@ -35,7 +35,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn {
3535 std.process.exit(0);
3636}
3737const std = @import("std");
38const io = std.Io.Threaded.global_single_threaded.ioBasic();
38const io = std.Io.Threaded.global_single_threaded.io();
3939#expect_stdout="new panic message: integer overflow\n"
4040
4141#update=change the panic handler function value
......@@ -53,5 +53,5 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn {
5353 std.process.exit(0);
5454}
5555const std = @import("std");
56const io = std.Io.Threaded.global_single_threaded.ioBasic();
56const io = std.Io.Threaded.global_single_threaded.io();
5757#expect_stdout="third panic message: integer overflow\n"
test/incremental/change_panic_handler_explicit+3-3
......@@ -47,7 +47,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn {
4747 std.process.exit(0);
4848}
4949const std = @import("std");
50const io = std.Io.Threaded.global_single_threaded.ioBasic();
50const io = std.Io.Threaded.global_single_threaded.io();
5151#expect_stdout="panic message: integer overflow\n"
5252
5353#update=change the panic handler body
......@@ -95,7 +95,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn {
9595 std.process.exit(0);
9696}
9797const std = @import("std");
98const io = std.Io.Threaded.global_single_threaded.ioBasic();
98const io = std.Io.Threaded.global_single_threaded.io();
9999#expect_stdout="new panic message: integer overflow\n"
100100
101101#update=change the panic handler function value
......@@ -143,5 +143,5 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn {
143143 std.process.exit(0);
144144}
145145const std = @import("std");
146const io = std.Io.Threaded.global_single_threaded.ioBasic();
146const io = std.Io.Threaded.global_single_threaded.io();
147147#expect_stdout="third panic message: integer overflow\n"
test/incremental/change_shift_op+2-2
......@@ -13,7 +13,7 @@ fn foo(x: u16) !void {
1313 try stdout_writer.interface.print("0x{x}\n", .{x << 4});
1414}
1515const std = @import("std");
16const io = std.Io.Threaded.global_single_threaded.ioBasic();
16const io = std.Io.Threaded.global_single_threaded.io();
1717#expect_stdout="0x3000\n"
1818#update=change to right shift
1919#file=main.zig
......@@ -25,5 +25,5 @@ fn foo(x: u16) !void {
2525 try stdout_writer.interface.print("0x{x}\n", .{x >> 4});
2626}
2727const std = @import("std");
28const io = std.Io.Threaded.global_single_threaded.ioBasic();
28const io = std.Io.Threaded.global_single_threaded.io();
2929#expect_stdout="0x130\n"
test/incremental/change_struct_same_fields+3-3
......@@ -18,7 +18,7 @@ fn foo(val: *const S) !void {
1818 );
1919}
2020const std = @import("std");
21const io = std.Io.Threaded.global_single_threaded.ioBasic();
21const io = std.Io.Threaded.global_single_threaded.io();
2222#expect_stdout="100 200\n"
2323
2424#update=change struct layout
......@@ -36,7 +36,7 @@ fn foo(val: *const S) !void {
3636 );
3737}
3838const std = @import("std");
39const io = std.Io.Threaded.global_single_threaded.ioBasic();
39const io = std.Io.Threaded.global_single_threaded.io();
4040#expect_stdout="100 200\n"
4141
4242#update=change values
......@@ -54,5 +54,5 @@ fn foo(val: *const S) !void {
5454 );
5555}
5656const std = @import("std");
57const io = std.Io.Threaded.global_single_threaded.ioBasic();
57const io = std.Io.Threaded.global_single_threaded.io();
5858#expect_stdout="1234 5678\n"
test/incremental/change_zon_file+3-3
......@@ -10,7 +10,7 @@ const message: []const u8 = @import("message.zon");
1010pub fn main() !void {
1111 try std.Io.File.stdout().writeStreamingAll(io, message);
1212}
13const io = std.Io.Threaded.global_single_threaded.ioBasic();
13const io = std.Io.Threaded.global_single_threaded.io();
1414#file=message.zon
1515"Hello, World!\n"
1616#expect_stdout="Hello, World!\n"
......@@ -32,7 +32,7 @@ const message: []const u8 = @import("message.zon");
3232pub fn main() !void {
3333 try std.Io.File.stdout().writeStreamingAll(io, "a hardcoded string\n");
3434}
35const io = std.Io.Threaded.global_single_threaded.ioBasic();
35const io = std.Io.Threaded.global_single_threaded.io();
3636#expect_error=message.zon:1:1: error: unable to load 'message.zon': FileNotFound
3737#expect_error=main.zig:2:37: note: file imported here
3838
......@@ -48,5 +48,5 @@ const message: []const u8 = @import("message.zon");
4848pub fn main() !void {
4949 try std.Io.File.stdout().writeStreamingAll(io, message);
5050}
51const io = std.Io.Threaded.global_single_threaded.ioBasic();
51const io = std.Io.Threaded.global_single_threaded.io();
5252#expect_stdout="We're back, World!\n"
test/incremental/change_zon_file_no_result_type+1-1
......@@ -6,7 +6,7 @@
66#update=initial version
77#file=main.zig
88const std = @import("std");
9const io = std.Io.Threaded.global_single_threaded.ioBasic();
9const io = std.Io.Threaded.global_single_threaded.io();
1010pub fn main() !void {
1111 try std.Io.File.stdout().writeStreamingAll(io, @import("foo.zon").message);
1212}
test/incremental/compile_log+3-3
......@@ -10,7 +10,7 @@ const std = @import("std");
1010pub fn main() !void {
1111 try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
1212}
13const io = std.Io.Threaded.global_single_threaded.ioBasic();
13const io = std.Io.Threaded.global_single_threaded.io();
1414#expect_stdout="Hello, World!\n"
1515
1616#update=add compile log
......@@ -20,7 +20,7 @@ pub fn main() !void {
2020 try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
2121 @compileLog("this is a log");
2222}
23const io = std.Io.Threaded.global_single_threaded.ioBasic();
23const io = std.Io.Threaded.global_single_threaded.io();
2424#expect_error=main.zig:4:5: error: found compile log statement
2525#expect_compile_log=@as(*const [13:0]u8, "this is a log")
2626
......@@ -30,5 +30,5 @@ const std = @import("std");
3030pub fn main() !void {
3131 try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
3232}
33const io = std.Io.Threaded.global_single_threaded.ioBasic();
33const io = std.Io.Threaded.global_single_threaded.io();
3434#expect_stdout="Hello, World!\n"
test/incremental/fix_astgen_failure+3-3
......@@ -19,7 +19,7 @@ const std = @import("std");
1919pub fn hello() !void {
2020 try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
2121}
22const io = std.Io.Threaded.global_single_threaded.ioBasic();
22const io = std.Io.Threaded.global_single_threaded.io();
2323#expect_stdout="Hello, World!\n"
2424#update=add new error
2525#file=foo.zig
......@@ -27,7 +27,7 @@ const std = @import("std");
2727pub fn hello() !void {
2828 try std.Io.File.stdout().writeStreamingAll(io, hello_str);
2929}
30const io = std.Io.Threaded.global_single_threaded.ioBasic();
30const io = std.Io.Threaded.global_single_threaded.io();
3131#expect_error=foo.zig:3:52: error: use of undeclared identifier 'hello_str'
3232#update=fix the new error
3333#file=foo.zig
......@@ -36,5 +36,5 @@ const hello_str = "Hello, World! Again!\n";
3636pub fn hello() !void {
3737 try std.Io.File.stdout().writeStreamingAll(io, hello_str);
3838}
39const io = std.Io.Threaded.global_single_threaded.ioBasic();
39const io = std.Io.Threaded.global_single_threaded.io();
4040#expect_stdout="Hello, World! Again!\n"
test/incremental/function_becomes_inline+3-3
......@@ -11,7 +11,7 @@ fn foo() !void {
1111 try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
1212}
1313const std = @import("std");
14const io = std.Io.Threaded.global_single_threaded.ioBasic();
14const io = std.Io.Threaded.global_single_threaded.io();
1515#expect_stdout="Hello, World!\n"
1616
1717#update=make function inline
......@@ -23,7 +23,7 @@ inline fn foo() !void {
2323 try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
2424}
2525const std = @import("std");
26const io = std.Io.Threaded.global_single_threaded.ioBasic();
26const io = std.Io.Threaded.global_single_threaded.io();
2727#expect_stdout="Hello, World!\n"
2828
2929#update=change string
......@@ -35,5 +35,5 @@ inline fn foo() !void {
3535 try std.Io.File.stdout().writeStreamingAll(io, "Hello, `inline` World!\n");
3636}
3737const std = @import("std");
38const io = std.Io.Threaded.global_single_threaded.ioBasic();
38const io = std.Io.Threaded.global_single_threaded.io();
3939#expect_stdout="Hello, `inline` World!\n"
test/incremental/hello+2-2
......@@ -6,7 +6,7 @@
66#update=initial version
77#file=main.zig
88const std = @import("std");
9const io = std.Io.Threaded.global_single_threaded.ioBasic();
9const io = std.Io.Threaded.global_single_threaded.io();
1010pub fn main() !void {
1111 try std.Io.File.stdout().writeStreamingAll(io, "good morning\n");
1212}
......@@ -14,7 +14,7 @@ pub fn main() !void {
1414#update=change the string
1515#file=main.zig
1616const std = @import("std");
17const io = std.Io.Threaded.global_single_threaded.ioBasic();
17const io = std.Io.Threaded.global_single_threaded.io();
1818pub fn main() !void {
1919 try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n");
2020}
test/incremental/make_decl_pub+2-2
......@@ -14,7 +14,7 @@ const std = @import("std");
1414fn hello() !void {
1515 try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
1616}
17const io = std.Io.Threaded.global_single_threaded.ioBasic();
17const io = std.Io.Threaded.global_single_threaded.io();
1818#expect_error=main.zig:3:12: error: 'hello' is not marked 'pub'
1919#expect_error=foo.zig:2:1: note: declared here
2020
......@@ -24,5 +24,5 @@ const std = @import("std");
2424pub fn hello() !void {
2525 try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n");
2626}
27const io = std.Io.Threaded.global_single_threaded.ioBasic();
27const io = std.Io.Threaded.global_single_threaded.io();
2828#expect_stdout="Hello, World!\n"
test/incremental/modify_inline_fn+2-2
......@@ -13,7 +13,7 @@ pub fn main() !void {
1313inline fn getStr() []const u8 {
1414 return "foo\n";
1515}
16const io = std.Io.Threaded.global_single_threaded.ioBasic();
16const io = std.Io.Threaded.global_single_threaded.io();
1717#expect_stdout="foo\n"
1818#update=change the string
1919#file=main.zig
......@@ -25,5 +25,5 @@ pub fn main() !void {
2525inline fn getStr() []const u8 {
2626 return "bar\n";
2727}
28const io = std.Io.Threaded.global_single_threaded.ioBasic();
28const io = std.Io.Threaded.global_single_threaded.io();
2929#expect_stdout="bar\n"
test/incremental/move_src+2-2
......@@ -16,7 +16,7 @@ fn foo() u32 {
1616fn bar() u32 {
1717 return 123;
1818}
19const io = std.Io.Threaded.global_single_threaded.ioBasic();
19const io = std.Io.Threaded.global_single_threaded.io();
2020#expect_stdout="7 123\n"
2121
2222#update=add newline
......@@ -33,5 +33,5 @@ fn foo() u32 {
3333fn bar() u32 {
3434 return 123;
3535}
36const io = std.Io.Threaded.global_single_threaded.ioBasic();
36const io = std.Io.Threaded.global_single_threaded.io();
3737#expect_stdout="8 123\n"
test/incremental/no_change_preserves_tag_names+2-2
......@@ -7,7 +7,7 @@
77#file=main.zig
88const std = @import("std");
99var some_enum: enum { first, second } = .first;
10const io = std.Io.Threaded.global_single_threaded.ioBasic();
10const io = std.Io.Threaded.global_single_threaded.io();
1111pub fn main() !void {
1212 try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum));
1313}
......@@ -16,7 +16,7 @@ pub fn main() !void {
1616#file=main.zig
1717const std = @import("std");
1818var some_enum: enum { first, second } = .first;
19const io = std.Io.Threaded.global_single_threaded.ioBasic();
19const io = std.Io.Threaded.global_single_threaded.io();
2020pub fn main() !void {
2121 try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum));
2222}
test/incremental/recursive_function_becomes_non_recursive+2-2
......@@ -14,7 +14,7 @@ fn foo(recurse: bool) !void {
1414 try stdout.writeStreamingAll(io, "non-recursive path\n");
1515}
1616const std = @import("std");
17const io = std.Io.Threaded.global_single_threaded.ioBasic();
17const io = std.Io.Threaded.global_single_threaded.io();
1818#expect_stdout="non-recursive path\n"
1919
2020#update=eliminate recursion and change argument
......@@ -28,5 +28,5 @@ fn foo(recurse: bool) !void {
2828 try stdout.writeStreamingAll(io, "non-recursive path\n");
2929}
3030const std = @import("std");
31const io = std.Io.Threaded.global_single_threaded.ioBasic();
31const io = std.Io.Threaded.global_single_threaded.io();
3232#expect_stdout="x==1\n"
test/incremental/remove_enum_field+2-2
......@@ -14,7 +14,7 @@ pub fn main() !void {
1414 try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)});
1515}
1616const std = @import("std");
17const io = std.Io.Threaded.global_single_threaded.ioBasic();
17const io = std.Io.Threaded.global_single_threaded.io();
1818#expect_stdout="1\n"
1919#update=remove enum field
2020#file=main.zig
......@@ -27,6 +27,6 @@ pub fn main() !void {
2727 try stdout_writer.interface.print("{}\n", .{@intFromEnum(MyEnum.foo)});
2828}
2929const std = @import("std");
30const io = std.Io.Threaded.global_single_threaded.ioBasic();
30const io = std.Io.Threaded.global_single_threaded.io();
3131#expect_error=main.zig:7:69: error: enum 'main.MyEnum' has no member named 'foo'
3232#expect_error=main.zig:1:16: note: enum declared here
test/incremental/unreferenced_error+4-4
......@@ -10,7 +10,7 @@ pub fn main() !void {
1010 try std.Io.File.stdout().writeStreamingAll(io, a);
1111}
1212const a = "Hello, World!\n";
13const io = std.Io.Threaded.global_single_threaded.ioBasic();
13const io = std.Io.Threaded.global_single_threaded.io();
1414#expect_stdout="Hello, World!\n"
1515
1616#update=introduce compile error
......@@ -20,7 +20,7 @@ pub fn main() !void {
2020 try std.Io.File.stdout().writeStreamingAll(io, a);
2121}
2222const a = @compileError("bad a");
23const io = std.Io.Threaded.global_single_threaded.ioBasic();
23const io = std.Io.Threaded.global_single_threaded.io();
2424#expect_error=main.zig:5:11: error: bad a
2525
2626#update=remove error reference
......@@ -31,7 +31,7 @@ pub fn main() !void {
3131}
3232const a = @compileError("bad a");
3333const b = "Hi there!\n";
34const io = std.Io.Threaded.global_single_threaded.ioBasic();
34const io = std.Io.Threaded.global_single_threaded.io();
3535#expect_stdout="Hi there!\n"
3636
3737#update=introduce and remove reference to error
......@@ -42,5 +42,5 @@ pub fn main() !void {
4242}
4343const a = "Back to a\n";
4444const b = @compileError("bad b");
45const io = std.Io.Threaded.global_single_threaded.ioBasic();
45const io = std.Io.Threaded.global_single_threaded.io();
4646#expect_stdout="Back to a\n"
test/standalone/coff_dwarf/build.zig+3
......@@ -46,6 +46,9 @@ pub fn build(b: *std.Build) void {
4646 lib.root_module.addCSourceFile(.{ .file = b.path("shared_lib.c"), .flags = &.{"-gdwarf"} });
4747 exe.root_module.linkLibrary(lib);
4848
49 if (target.result.os.tag == .windows)
50 exe.root_module.linkSystemLibrary("ws2_32", .{});
51
4952 const run = b.addRunArtifact(exe);
5053 run.expectExitCode(0);
5154 run.skip_foreign_checks = true;
test/standalone/dirname/exists_in.zig+1-1
......@@ -26,7 +26,7 @@ pub fn main(init: std.process.Init) !void {
2626 return error.BadUsage;
2727 };
2828
29 const io = std.Io.Threaded.global_single_threaded.ioBasic();
29 const io = std.Io.Threaded.global_single_threaded.io();
3030
3131 var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{});
3232 defer dir.close(io);
test/standalone/dirname/touch.zig+1-1
......@@ -21,7 +21,7 @@ pub fn main(init: std.process.Init) !void {
2121 const dir_path = std.Io.Dir.path.dirname(path) orelse unreachable;
2222 const basename = std.Io.Dir.path.basename(path);
2323
24 const io = std.Io.Threaded.global_single_threaded.ioBasic();
24 const io = std.Io.Threaded.global_single_threaded.io();
2525
2626 var dir = try std.Io.Dir.cwd().openDir(io, dir_path, .{});
2727 defer dir.close(io);
test/standalone/issue_5825/build.zig+1
......@@ -34,6 +34,7 @@ pub fn build(b: *std.Build) void {
3434 exe.subsystem = .console;
3535 exe.root_module.linkSystemLibrary("kernel32", .{});
3636 exe.root_module.linkSystemLibrary("ntdll", .{});
37 exe.root_module.linkSystemLibrary("ws2_32", .{});
3738 exe.root_module.addObject(obj);
3839
3940 // TODO: actually check the output
test/standalone/mix_o_files/build.zig+3
......@@ -16,6 +16,9 @@ pub fn build(b: *std.Build) void {
1616 }),
1717 });
1818
19 if (target.result.os.tag == .windows)
20 obj.root_module.linkSystemLibrary("ws2_32", .{});
21
1922 const exe = b.addExecutable(.{
2023 .name = "test",
2124 .root_module = b.createModule(.{
test/standalone/run_cwd/check_file_exists.zig+1-1
......@@ -5,7 +5,7 @@ pub fn main(init: std.process.Init) !void {
55 if (args.len != 2) return error.BadUsage;
66 const path = args[1];
77
8 const io = std.Io.Threaded.global_single_threaded.ioBasic();
8 const io = std.Io.Threaded.global_single_threaded.io();
99
1010 std.Io.Dir.cwd().access(io, path, .{}) catch return error.AccessFailed;
1111}
test/standalone/shared_library/build.zig+4-1
......@@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void {
55 b.default_step = test_step;
66
77 const optimize: std.builtin.OptimizeMode = .Debug;
8 const target = b.graph.host;
8 const target = b.standardTargetOptions(.{});
99
1010 const exe_names: []const []const u8 = &.{ "test", "test-dync" };
1111 const lib_names: []const []const u8 = &.{ "mathtest", "mathtest-dync" };
......@@ -24,6 +24,9 @@ pub fn build(b: *std.Build) void {
2424 }),
2525 });
2626
27 if (target.result.os.tag == .windows)
28 lib.root_module.linkSystemLibrary("ws2_32", .{});
29
2730 const exe = b.addExecutable(.{
2831 .name = exe_name,
2932 .root_module = b.createModule(.{
test/standalone/windows_argv/build.zig+3
......@@ -20,6 +20,8 @@ pub fn build(b: *std.Build) !void {
2020 .optimize = optimize,
2121 }),
2222 });
23 lib_gnu.root_module.linkSystemLibrary("ws2_32", .{});
24
2325 const verify_gnu = b.addExecutable(.{
2426 .name = "verify-gnu",
2527 .root_module = b.createModule(.{
......@@ -101,6 +103,7 @@ pub fn build(b: *std.Build) !void {
101103 .flags = &.{ "-DUNICODE", "-D_UNICODE" },
102104 });
103105 verify_msvc.root_module.linkLibrary(lib_msvc);
106 verify_msvc.root_module.linkSystemLibrary("ws2_32", .{});
104107 verify_msvc.root_module.link_libc = true;
105108
106109 const run_msvc = b.addRunArtifact(fuzz);