authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-23 13:58:49-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:51-07:00
log46f7e3ea9fff4b3f83cab58c20625d3961be6020
treec8cba4670ad23bc0c9a66d31b31ec5e847b0dc39
parent701d4bc80b8cbb649364e8ecf86ed9cea8af739d

std.Io.Threaded: add ioBasic which disables networking


11 files changed, 375 insertions(+), 118 deletions(-)

lib/std/Io/Threaded.zig+333-89
......@@ -168,80 +168,28 @@ pub fn io(t: *Threaded) Io {
168168 .conditionWaitUncancelable = conditionWaitUncancelable,
169169 .conditionWake = conditionWake,
170170
171 .dirMake = switch (native_os) {
172 .windows => dirMakeWindows,
173 .wasi => dirMakeWasi,
174 else => dirMakePosix,
175 },
176 .dirMakePath = switch (native_os) {
177 .windows => dirMakePathWindows,
178 else => dirMakePathPosix,
179 },
180 .dirMakeOpenPath = switch (native_os) {
181 .windows => dirMakeOpenPathWindows,
182 .wasi => dirMakeOpenPathWasi,
183 else => dirMakeOpenPathPosix,
184 },
171 .dirMake = dirMake,
172 .dirMakePath = dirMakePath,
173 .dirMakeOpenPath = dirMakeOpenPath,
185174 .dirStat = dirStat,
186 .dirStatPath = switch (native_os) {
187 .linux => dirStatPathLinux,
188 .windows => dirStatPathWindows,
189 .wasi => dirStatPathWasi,
190 else => dirStatPathPosix,
191 },
192 .fileStat = switch (native_os) {
193 .linux => fileStatLinux,
194 .windows => fileStatWindows,
195 .wasi => fileStatWasi,
196 else => fileStatPosix,
197 },
198 .dirAccess = switch (native_os) {
199 .windows => dirAccessWindows,
200 .wasi => dirAccessWasi,
201 else => dirAccessPosix,
202 },
203 .dirCreateFile = switch (native_os) {
204 .windows => dirCreateFileWindows,
205 .wasi => dirCreateFileWasi,
206 else => dirCreateFilePosix,
207 },
208 .dirOpenFile = switch (native_os) {
209 .windows => dirOpenFileWindows,
210 .wasi => dirOpenFileWasi,
211 else => dirOpenFilePosix,
212 },
213 .dirOpenDir = switch (native_os) {
214 .wasi => dirOpenDirWasi,
215 .haiku => dirOpenDirHaiku,
216 else => dirOpenDirPosix,
217 },
175 .dirStatPath = dirStatPath,
176 .fileStat = fileStat,
177 .dirAccess = dirAccess,
178 .dirCreateFile = dirCreateFile,
179 .dirOpenFile = dirOpenFile,
180 .dirOpenDir = dirOpenDir,
218181 .dirClose = dirClose,
219182 .fileClose = fileClose,
220183 .fileWriteStreaming = fileWriteStreaming,
221184 .fileWritePositional = fileWritePositional,
222 .fileReadStreaming = switch (native_os) {
223 .windows => fileReadStreamingWindows,
224 else => fileReadStreamingPosix,
225 },
226 .fileReadPositional = switch (native_os) {
227 .windows => fileReadPositionalWindows,
228 else => fileReadPositionalPosix,
229 },
185 .fileReadStreaming = fileReadStreaming,
186 .fileReadPositional = fileReadPositional,
230187 .fileSeekBy = fileSeekBy,
231188 .fileSeekTo = fileSeekTo,
232189 .openSelfExe = openSelfExe,
233190
234 .now = switch (native_os) {
235 .windows => nowWindows,
236 .wasi => nowWasi,
237 else => nowPosix,
238 },
239 .sleep = switch (native_os) {
240 .windows => sleepWindows,
241 .wasi => sleepWasi,
242 .linux => sleepLinux,
243 else => sleepPosix,
244 },
191 .now = now,
192 .sleep = sleep,
245193
246194 .netListenIp = switch (native_os) {
247195 .windows => netListenIpWindows,
......@@ -291,6 +239,73 @@ pub fn io(t: *Threaded) Io {
291239 };
292240}
293241
242/// Same as `io` but disables all networking functionality, which has
243/// an additional dependency on Windows (ws2_32).
244pub fn ioBasic(t: *Threaded) Io {
245 return .{
246 .userdata = t,
247 .vtable = &.{
248 .async = async,
249 .concurrent = concurrent,
250 .await = await,
251 .cancel = cancel,
252 .cancelRequested = cancelRequested,
253 .select = select,
254
255 .groupAsync = groupAsync,
256 .groupWait = groupWait,
257 .groupWaitUncancelable = groupWaitUncancelable,
258 .groupCancel = groupCancel,
259
260 .mutexLock = mutexLock,
261 .mutexLockUncancelable = mutexLockUncancelable,
262 .mutexUnlock = mutexUnlock,
263
264 .conditionWait = conditionWait,
265 .conditionWaitUncancelable = conditionWaitUncancelable,
266 .conditionWake = conditionWake,
267
268 .dirMake = dirMake,
269 .dirMakePath = dirMakePath,
270 .dirMakeOpenPath = dirMakeOpenPath,
271 .dirStat = dirStat,
272 .dirStatPath = dirStatPath,
273 .fileStat = fileStat,
274 .dirAccess = dirAccess,
275 .dirCreateFile = dirCreateFile,
276 .dirOpenFile = dirOpenFile,
277 .dirOpenDir = dirOpenDir,
278 .dirClose = dirClose,
279 .fileClose = fileClose,
280 .fileWriteStreaming = fileWriteStreaming,
281 .fileWritePositional = fileWritePositional,
282 .fileReadStreaming = fileReadStreaming,
283 .fileReadPositional = fileReadPositional,
284 .fileSeekBy = fileSeekBy,
285 .fileSeekTo = fileSeekTo,
286 .openSelfExe = openSelfExe,
287
288 .now = now,
289 .sleep = sleep,
290
291 .netListenIp = netListenIpUnavailable,
292 .netListenUnix = netListenUnixUnavailable,
293 .netAccept = netAcceptUnavailable,
294 .netBindIp = netBindIpUnavailable,
295 .netConnectIp = netConnectIpUnavailable,
296 .netConnectUnix = netConnectUnixUnavailable,
297 .netClose = netCloseUnavailable,
298 .netRead = netReadUnavailable,
299 .netWrite = netWriteUnavailable,
300 .netSend = netSendUnavailable,
301 .netReceive = netReceiveUnavailable,
302 .netInterfaceNameResolve = netInterfaceNameResolveUnavailable,
303 .netInterfaceName = netInterfaceNameUnavailable,
304 .netLookup = netLookupUnavailable,
305 },
306 };
307}
308
294309pub const socket_flags_unsupported = native_os.isDarwin() or native_os == .haiku; // 💩💩
295310const have_accept4 = !socket_flags_unsupported;
296311const have_flock_open_flags = @hasField(posix.O, "EXLOCK");
......@@ -804,7 +819,7 @@ fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mut
804819fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) void {
805820 if (builtin.single_threaded) unreachable; // Deadlock.
806821 const t: *Threaded = @ptrCast(@alignCast(userdata));
807 const t_io = t.io();
822 const t_io = ioBasic(t);
808823 comptime assert(@TypeOf(cond.state) == u64);
809824 const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state);
810825 const cond_state = &ints[0];
......@@ -835,6 +850,7 @@ fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex:
835850fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void {
836851 if (builtin.single_threaded) unreachable; // Deadlock.
837852 const t: *Threaded = @ptrCast(@alignCast(userdata));
853 const t_io = ioBasic(t);
838854 comptime assert(@TypeOf(cond.state) == u64);
839855 const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state);
840856 const cond_state = &ints[0];
......@@ -858,8 +874,8 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I
858874 assert(state & waiter_mask != waiter_mask);
859875 state += one_waiter;
860876
861 mutex.unlock(t.io());
862 defer mutex.lockUncancelable(t.io());
877 mutex.unlock(t_io);
878 defer mutex.lockUncancelable(t_io);
863879
864880 while (true) {
865881 try futexWait(t, cond_epoch, epoch);
......@@ -939,6 +955,12 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.
939955 }
940956}
941957
958const dirMake = switch (native_os) {
959 .windows => dirMakeWindows,
960 .wasi => dirMakeWasi,
961 else => dirMakePosix,
962};
963
942964fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {
943965 const t: *Threaded = @ptrCast(@alignCast(userdata));
944966
......@@ -1027,6 +1049,11 @@ fn dirMakeWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode
10271049 windows.CloseHandle(sub_dir_handle);
10281050}
10291051
1052const dirMakePath = switch (native_os) {
1053 .windows => dirMakePathWindows,
1054 else => dirMakePathPosix,
1055};
1056
10301057fn dirMakePathPosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {
10311058 const t: *Threaded = @ptrCast(@alignCast(userdata));
10321059 _ = t;
......@@ -1045,6 +1072,12 @@ fn dirMakePathWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8,
10451072 @panic("TODO implement dirMakePathWindows");
10461073}
10471074
1075const dirMakeOpenPath = switch (native_os) {
1076 .windows => dirMakeOpenPathWindows,
1077 .wasi => dirMakeOpenPathWasi,
1078 else => dirMakeOpenPathPosix,
1079};
1080
10481081fn dirMakeOpenPathPosix(
10491082 userdata: ?*anyopaque,
10501083 dir: Io.Dir,
......@@ -1052,11 +1085,11 @@ fn dirMakeOpenPathPosix(
10521085 options: Io.Dir.OpenOptions,
10531086) Io.Dir.MakeOpenPathError!Io.Dir {
10541087 const t: *Threaded = @ptrCast(@alignCast(userdata));
1055 const t_io = t.io();
1056 return dir.openDir(t_io, sub_path, options) catch |err| switch (err) {
1088 const t_io = ioBasic(t);
1089 return dirOpenDirPosix(t, dir, sub_path, options) catch |err| switch (err) {
10571090 error.FileNotFound => {
10581091 try dir.makePath(t_io, sub_path);
1059 return dir.openDir(t_io, sub_path, options);
1092 return dirOpenDirPosix(t, dir, sub_path, options);
10601093 },
10611094 else => |e| return e,
10621095 };
......@@ -1135,7 +1168,7 @@ fn dirMakeOpenPathWindows(
11351168 // could cause an infinite loop
11361169 check_dir: {
11371170 // workaround for windows, see https://github.com/ziglang/zig/issues/16738
1138 const fstat = dir.statPath(t.io(), component.path, .{
1171 const fstat = dirStatPathWindows(t, dir, component.path, .{
11391172 .follow_symlinks = options.follow_symlinks,
11401173 }) catch |stat_err| switch (stat_err) {
11411174 error.IsDir => break :check_dir,
......@@ -1187,6 +1220,13 @@ fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {
11871220 @panic("TODO implement dirStat");
11881221}
11891222
1223const dirStatPath = switch (native_os) {
1224 .linux => dirStatPathLinux,
1225 .windows => dirStatPathWindows,
1226 .wasi => dirStatPathWasi,
1227 else => dirStatPathPosix,
1228};
1229
11901230fn dirStatPathLinux(
11911231 userdata: ?*anyopaque,
11921232 dir: Io.Dir,
......@@ -1275,12 +1315,11 @@ fn dirStatPathWindows(
12751315 options: Io.Dir.StatPathOptions,
12761316) Io.Dir.StatPathError!Io.File.Stat {
12771317 const t: *Threaded = @ptrCast(@alignCast(userdata));
1278 const t_io = t.io();
1279 var file = try dir.openFile(t_io, sub_path, .{
1318 const file = try dirOpenFileWindows(t, dir, sub_path, .{
12801319 .follow_symlinks = options.follow_symlinks,
12811320 });
1282 defer file.close(t_io);
1283 return file.stat(t_io);
1321 defer windows.CloseHandle(file.handle);
1322 return fileStatWindows(t, file);
12841323}
12851324
12861325fn dirStatPathWasi(
......@@ -1318,6 +1357,13 @@ fn dirStatPathWasi(
13181357 }
13191358}
13201359
1360const fileStat = switch (native_os) {
1361 .linux => fileStatLinux,
1362 .windows => fileStatWindows,
1363 .wasi => fileStatWasi,
1364 else => fileStatPosix,
1365};
1366
13211367fn fileStatPosix(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
13221368 const t: *Threaded = @ptrCast(@alignCast(userdata));
13231369
......@@ -1440,6 +1486,12 @@ fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.
14401486 }
14411487}
14421488
1489const dirAccess = switch (native_os) {
1490 .windows => dirAccessWindows,
1491 .wasi => dirAccessWasi,
1492 else => dirAccessPosix,
1493};
1494
14431495fn dirAccessPosix(
14441496 userdata: ?*anyopaque,
14451497 dir: Io.Dir,
......@@ -1589,6 +1641,12 @@ fn dirAccessWindows(
15891641 }
15901642}
15911643
1644const dirCreateFile = switch (native_os) {
1645 .windows => dirCreateFileWindows,
1646 .wasi => dirCreateFileWasi,
1647 else => dirCreateFilePosix,
1648};
1649
15921650fn dirCreateFilePosix(
15931651 userdata: ?*anyopaque,
15941652 dir: Io.Dir,
......@@ -1827,6 +1885,12 @@ fn dirCreateFileWasi(
18271885 }
18281886}
18291887
1888const dirOpenFile = switch (native_os) {
1889 .windows => dirOpenFileWindows,
1890 .wasi => dirOpenFileWasi,
1891 else => dirOpenFilePosix,
1892};
1893
18301894fn dirOpenFilePosix(
18311895 userdata: ?*anyopaque,
18321896 dir: Io.Dir,
......@@ -2077,6 +2141,12 @@ fn dirOpenFileWasi(
20772141 }
20782142}
20792143
2144const dirOpenDir = switch (native_os) {
2145 .wasi => dirOpenDirWasi,
2146 .haiku => dirOpenDirHaiku,
2147 else => dirOpenDirPosix,
2148};
2149
20802150fn dirOpenDirPosix(
20812151 userdata: ?*anyopaque,
20822152 dir: Io.Dir,
......@@ -2320,6 +2390,11 @@ fn fileClose(userdata: ?*anyopaque, file: Io.File) void {
23202390 posix.close(file.handle);
23212391}
23222392
2393const fileReadStreaming = switch (native_os) {
2394 .windows => fileReadStreamingWindows,
2395 else => fileReadStreamingPosix,
2396};
2397
23232398fn fileReadStreamingPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.ReadStreamingError!usize {
23242399 const t: *Threaded = @ptrCast(@alignCast(userdata));
23252400
......@@ -2482,6 +2557,11 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8, o
24822557 }
24832558}
24842559
2560const fileReadPositional = switch (native_os) {
2561 .windows => fileReadPositionalWindows,
2562 else => fileReadPositionalPosix,
2563};
2564
24852565fn fileReadPositionalWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize {
24862566 const t: *Threaded = @ptrCast(@alignCast(userdata));
24872567 try t.checkCancel();
......@@ -2652,6 +2732,12 @@ fn nowPosix(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp
26522732 }
26532733}
26542734
2735const now = switch (native_os) {
2736 .windows => nowWindows,
2737 .wasi => nowWasi,
2738 else => nowPosix,
2739};
2740
26552741fn nowWindows(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
26562742 const t: *Threaded = @ptrCast(@alignCast(userdata));
26572743 _ = t;
......@@ -2680,6 +2766,13 @@ fn nowWasi(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
26802766 return .fromNanoseconds(ns);
26812767}
26822768
2769const sleep = switch (native_os) {
2770 .windows => sleepWindows,
2771 .wasi => sleepWasi,
2772 .linux => sleepLinux,
2773 else => sleepPosix,
2774};
2775
26832776fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
26842777 const t: *Threaded = @ptrCast(@alignCast(userdata));
26852778 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
......@@ -2710,9 +2803,10 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
27102803
27112804fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
27122805 const t: *Threaded = @ptrCast(@alignCast(userdata));
2806 const t_io = ioBasic(t);
27132807 try t.checkCancel();
27142808 const ms = ms: {
2715 const d = (try timeout.toDurationFromNow(t.io())) orelse
2809 const d = (try timeout.toDurationFromNow(t_io)) orelse
27162810 break :ms std.math.maxInt(windows.DWORD);
27172811 break :ms std.math.lossyCast(windows.DWORD, d.raw.toMilliseconds());
27182812 };
......@@ -2721,11 +2815,12 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
27212815
27222816fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
27232817 const t: *Threaded = @ptrCast(@alignCast(userdata));
2818 const t_io = ioBasic(t);
27242819 try t.checkCancel();
27252820
27262821 const w = std.os.wasi;
27272822
2728 const clock: w.subscription_clock_t = if (try timeout.toDurationFromNow(t.io())) |d| .{
2823 const clock: w.subscription_clock_t = if (try timeout.toDurationFromNow(t_io)) |d| .{
27292824 .id = clockToWasi(d.clock),
27302825 .timeout = std.math.lossyCast(u64, d.raw.nanoseconds),
27312826 .precision = 0,
......@@ -2750,11 +2845,12 @@ fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
27502845
27512846fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
27522847 const t: *Threaded = @ptrCast(@alignCast(userdata));
2848 const t_io = ioBasic(t);
27532849 const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;
27542850 const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;
27552851
27562852 var timespec: posix.timespec = t: {
2757 const d = (try timeout.toDurationFromNow(t.io())) orelse break :t .{
2853 const d = (try timeout.toDurationFromNow(t_io)) orelse break :t .{
27582854 .sec = std.math.maxInt(sec_type),
27592855 .nsec = std.math.maxInt(nsec_type),
27602856 };
......@@ -2919,6 +3015,17 @@ fn netListenIpWindows(
29193015 };
29203016}
29213017
3018fn netListenIpUnavailable(
3019 userdata: ?*anyopaque,
3020 address: IpAddress,
3021 options: IpAddress.ListenOptions,
3022) IpAddress.ListenError!net.Server {
3023 _ = userdata;
3024 _ = address;
3025 _ = options;
3026 return error.NetworkDown;
3027}
3028
29223029fn netListenUnixPosix(
29233030 userdata: ?*anyopaque,
29243031 address: *const net.UnixAddress,
......@@ -2965,6 +3072,17 @@ fn netListenUnixWindows(
29653072 @panic("TODO implement netListenUnixWindows");
29663073}
29673074
3075fn netListenUnixUnavailable(
3076 userdata: ?*anyopaque,
3077 address: *const net.UnixAddress,
3078 options: net.UnixAddress.ListenOptions,
3079) net.UnixAddress.ListenError!net.Socket.Handle {
3080 _ = userdata;
3081 _ = address;
3082 _ = options;
3083 return error.AddressFamilyUnsupported;
3084}
3085
29683086fn posixBindUnix(t: *Threaded, fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void {
29693087 while (true) {
29703088 try t.checkCancel();
......@@ -3235,6 +3353,17 @@ fn netConnectIpWindows(
32353353 } };
32363354}
32373355
3356fn netConnectIpUnavailable(
3357 userdata: ?*anyopaque,
3358 address: *const IpAddress,
3359 options: IpAddress.ConnectOptions,
3360) IpAddress.ConnectError!net.Stream {
3361 _ = userdata;
3362 _ = address;
3363 _ = options;
3364 return error.NetworkDown;
3365}
3366
32383367fn netConnectUnixPosix(
32393368 userdata: ?*anyopaque,
32403369 address: *const net.UnixAddress,
......@@ -3263,6 +3392,15 @@ fn netConnectUnixWindows(
32633392 @panic("TODO implement netConnectUnixWindows");
32643393}
32653394
3395fn netConnectUnixUnavailable(
3396 userdata: ?*anyopaque,
3397 address: *const net.UnixAddress,
3398) net.UnixAddress.ConnectError!net.Socket.Handle {
3399 _ = userdata;
3400 _ = address;
3401 return error.AddressFamilyUnsupported;
3402}
3403
32663404fn netBindIpPosix(
32673405 userdata: ?*anyopaque,
32683406 address: *const IpAddress,
......@@ -3330,6 +3468,17 @@ fn netBindIpWindows(
33303468 };
33313469}
33323470
3471fn netBindIpUnavailable(
3472 userdata: ?*anyopaque,
3473 address: *const IpAddress,
3474 options: IpAddress.BindOptions,
3475) IpAddress.BindError!net.Socket {
3476 _ = userdata;
3477 _ = address;
3478 _ = options;
3479 return error.NetworkDown;
3480}
3481
33333482fn openSocketPosix(
33343483 t: *Threaded,
33353484 family: posix.sa_family_t,
......@@ -3498,7 +3647,14 @@ fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net
34983647 }
34993648}
35003649
3650fn netAcceptUnavailable(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream {
3651 _ = userdata;
3652 _ = listen_handle;
3653 return error.NetworkDown;
3654}
3655
35013656fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
3657 if (!have_networking) return error.NetworkDown;
35023658 const t: *Threaded = @ptrCast(@alignCast(userdata));
35033659
35043660 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
......@@ -3560,7 +3716,7 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.
35603716}
35613717
35623718fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
3563 if (!have_networking) return .{ error.NetworkDown, 0 };
3719 if (!have_networking) return error.NetworkDown;
35643720 const t: *Threaded = @ptrCast(@alignCast(userdata));
35653721 _ = t;
35663722 _ = handle;
......@@ -3568,6 +3724,13 @@ fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8
35683724 @panic("TODO implement netReadWindows");
35693725}
35703726
3727fn netReadUnavailable(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
3728 _ = userdata;
3729 _ = fd;
3730 _ = data;
3731 return error.NetworkDown;
3732}
3733
35713734fn netSendPosix(
35723735 userdata: ?*anyopaque,
35733736 handle: net.Socket.Handle,
......@@ -3612,6 +3775,19 @@ fn netSendWindows(
36123775 @panic("TODO netSendWindows");
36133776}
36143777
3778fn netSendUnavailable(
3779 userdata: ?*anyopaque,
3780 handle: net.Socket.Handle,
3781 messages: []net.OutgoingMessage,
3782 flags: net.SendFlags,
3783) struct { ?net.Socket.SendError, usize } {
3784 _ = userdata;
3785 _ = handle;
3786 _ = messages;
3787 _ = flags;
3788 return .{ error.NetworkDown, 0 };
3789}
3790
36153791fn netSendOne(
36163792 t: *Threaded,
36173793 handle: net.Socket.Handle,
......@@ -3777,6 +3953,7 @@ fn netReceivePosix(
37773953) struct { ?net.Socket.ReceiveTimeoutError, usize } {
37783954 if (!have_networking) return .{ error.NetworkDown, 0 };
37793955 const t: *Threaded = @ptrCast(@alignCast(userdata));
3956 const t_io = io(t);
37803957
37813958 // recvmmsg is useless, here's why:
37823959 // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371)
......@@ -3803,7 +3980,7 @@ fn netReceivePosix(
38033980 var message_i: usize = 0;
38043981 var data_i: usize = 0;
38053982
3806 const deadline = timeout.toDeadline(t.io()) catch |err| return .{ err, message_i };
3983 const deadline = timeout.toDeadline(t_io) catch |err| return .{ err, message_i };
38073984
38083985 recv: while (true) {
38093986 t.checkCancel() catch |err| return .{ err, message_i };
......@@ -3849,7 +4026,7 @@ fn netReceivePosix(
38494026
38504027 const max_poll_ms = std.math.maxInt(u31);
38514028 const timeout_ms: u31 = if (deadline) |d| t: {
3852 const duration = d.durationFromNow(t.io()) catch |err| return .{ err, message_i };
4029 const duration = d.durationFromNow(t_io) catch |err| return .{ err, message_i };
38534030 if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i };
38544031 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));
38554032 } else max_poll_ms;
......@@ -3915,6 +4092,23 @@ fn netReceiveWindows(
39154092 @panic("TODO implement netReceiveWindows");
39164093}
39174094
4095fn netReceiveUnavailable(
4096 userdata: ?*anyopaque,
4097 handle: net.Socket.Handle,
4098 message_buffer: []net.IncomingMessage,
4099 data_buffer: []u8,
4100 flags: net.ReceiveFlags,
4101 timeout: Io.Timeout,
4102) struct { ?net.Socket.ReceiveTimeoutError, usize } {
4103 _ = userdata;
4104 _ = handle;
4105 _ = message_buffer;
4106 _ = data_buffer;
4107 _ = flags;
4108 _ = timeout;
4109 return .{ error.NetworkDown, 0 };
4110}
4111
39184112fn netWritePosix(
39194113 userdata: ?*anyopaque,
39204114 fd: net.Socket.Handle,
......@@ -4013,6 +4207,21 @@ fn netWriteWindows(
40134207 @panic("TODO implement netWriteWindows");
40144208}
40154209
4210fn netWriteUnavailable(
4211 userdata: ?*anyopaque,
4212 handle: net.Socket.Handle,
4213 header: []const u8,
4214 data: []const []const u8,
4215 splat: usize,
4216) net.Stream.Writer.Error!usize {
4217 _ = userdata;
4218 _ = handle;
4219 _ = header;
4220 _ = data;
4221 _ = splat;
4222 return error.NetworkDown;
4223}
4224
40164225fn addBuf(v: []posix.iovec_const, i: *@FieldType(posix.msghdr_const, "iovlen"), bytes: []const u8) void {
40174226 // OS checks ptr addr before length so zero length vectors must be omitted.
40184227 if (bytes.len == 0) return;
......@@ -4030,6 +4239,12 @@ fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void {
40304239 }
40314240}
40324241
4242fn netCloseUnavailable(userdata: ?*anyopaque, handle: net.Socket.Handle) void {
4243 _ = userdata;
4244 _ = handle;
4245 unreachable; // How you gonna close something that was impossible to open?
4246}
4247
40334248fn netInterfaceNameResolve(
40344249 userdata: ?*anyopaque,
40354250 name: *const net.Interface.Name,
......@@ -4089,6 +4304,15 @@ fn netInterfaceNameResolve(
40894304 @panic("unimplemented");
40904305}
40914306
4307fn netInterfaceNameResolveUnavailable(
4308 userdata: ?*anyopaque,
4309 name: *const net.Interface.Name,
4310) net.Interface.Name.ResolveError!net.Interface {
4311 _ = userdata;
4312 _ = name;
4313 return error.InterfaceNotFound;
4314}
4315
40924316fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
40934317 const t: *Threaded = @ptrCast(@alignCast(userdata));
40944318 try t.checkCancel();
......@@ -4109,6 +4333,12 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa
41094333 @panic("unimplemented");
41104334}
41114335
4336fn netInterfaceNameUnavailable(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
4337 _ = userdata;
4338 _ = interface;
4339 return error.Unexpected;
4340}
4341
41124342fn netLookup(
41134343 userdata: ?*anyopaque,
41144344 host_name: HostName,
......@@ -4116,17 +4346,31 @@ fn netLookup(
41164346 options: HostName.LookupOptions,
41174347) void {
41184348 const t: *Threaded = @ptrCast(@alignCast(userdata));
4119 const t_io = t.io();
4349 const t_io = io(t);
41204350 resolved.putOneUncancelable(t_io, .{ .end = netLookupFallible(t, host_name, resolved, options) });
41214351}
41224352
4353fn netLookupUnavailable(
4354 userdata: ?*anyopaque,
4355 host_name: HostName,
4356 resolved: *Io.Queue(HostName.LookupResult),
4357 options: HostName.LookupOptions,
4358) void {
4359 _ = host_name;
4360 _ = options;
4361 const t: *Threaded = @ptrCast(@alignCast(userdata));
4362 const t_io = ioBasic(t);
4363 resolved.putOneUncancelable(t_io, .{ .end = error.NetworkDown });
4364}
4365
41234366fn netLookupFallible(
41244367 t: *Threaded,
41254368 host_name: HostName,
41264369 resolved: *Io.Queue(HostName.LookupResult),
41274370 options: HostName.LookupOptions,
41284371) !void {
4129 const t_io = t.io();
4372 if (!have_networking) return error.NetworkDown;
4373 const t_io = io(t);
41304374 const name = host_name.bytes;
41314375 assert(name.len <= HostName.max_len);
41324376
......@@ -4637,7 +4881,7 @@ fn lookupDnsSearch(
46374881 resolved: *Io.Queue(HostName.LookupResult),
46384882 options: HostName.LookupOptions,
46394883) HostName.LookupError!void {
4640 const t_io = t.io();
4884 const t_io = io(t);
46414885 const rc = HostName.ResolvConf.init(t_io) catch return error.ResolvConfParseFailed;
46424886
46434887 // Count dots, suppress search when >=ndots or name ends in
......@@ -4681,7 +4925,7 @@ fn lookupDns(
46814925 resolved: *Io.Queue(HostName.LookupResult),
46824926 options: HostName.LookupOptions,
46834927) HostName.LookupError!void {
4684 const t_io = t.io();
4928 const t_io = io(t);
46854929 const family_records: [2]struct { af: IpAddress.Family, rr: HostName.DnsRecord } = .{
46864930 .{ .af = .ip6, .rr = .A },
46874931 .{ .af = .ip4, .rr = .AAAA },
......@@ -4868,7 +5112,7 @@ fn lookupHosts(
48685112 resolved: *Io.Queue(HostName.LookupResult),
48695113 options: HostName.LookupOptions,
48705114) !void {
4871 const t_io = t.io();
5115 const t_io = io(t);
48725116 const file = Io.File.openAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) {
48735117 error.FileNotFound,
48745118 error.NotDir,
......@@ -4906,7 +5150,7 @@ fn lookupHostsReader(
49065150 options: HostName.LookupOptions,
49075151 reader: *Io.Reader,
49085152) error{ ReadFailed, Canceled, UnknownHostName }!void {
4909 const t_io = t.io();
5153 const t_io = io(t);
49105154 var addresses_len: usize = 0;
49115155 var canonical_name: ?HostName = null;
49125156 while (true) {
......@@ -5374,7 +5618,7 @@ const Wsa = struct {
53745618};
53755619
53765620fn initializeWsa(t: *Threaded) error{NetworkDown}!void {
5377 const t_io = t.io();
5621 const t_io = io(t);
53785622 const wsa = &t.wsa;
53795623 wsa.mutex.lockUncancelable(t_io);
53805624 defer wsa.mutex.unlock(t_io);
lib/std/Thread.zig+1-1
......@@ -320,7 +320,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
320320 const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
321321
322322 var threaded: std.Io.Threaded = .init_single_threaded;
323 const io = threaded.io();
323 const io = threaded.ioBasic();
324324
325325 const file = try std.fs.cwd().openFile(path, .{});
326326 defer file.close();
lib/std/debug.zig+3-3
......@@ -649,7 +649,7 @@ pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf:
649649/// See `captureCurrentStackTrace` to capture the trace addresses into a buffer instead of printing.
650650pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_config: tty.Config) Writer.Error!void {
651651 var threaded: Io.Threaded = .init_single_threaded;
652 const io = threaded.io();
652 const io = threaded.ioBasic();
653653
654654 if (!std.options.allow_stack_tracing) {
655655 tty_config.setColor(writer, .dim) catch {};
......@@ -780,7 +780,7 @@ pub fn writeStackTrace(st: *const StackTrace, writer: *Writer, tty_config: tty.C
780780 // We use an independent Io implementation here in case there was a problem
781781 // with the application's Io implementation itself.
782782 var threaded: Io.Threaded = .init_single_threaded;
783 const io = threaded.io();
783 const io = threaded.ioBasic();
784784
785785 // Fetch `st.index` straight away. Aside from avoiding redundant loads, this prevents issues if
786786 // `st` is `@errorReturnTrace()` and errors are encountered while writing the stack trace.
......@@ -1602,7 +1602,7 @@ test "manage resources correctly" {
16021602 };
16031603 const gpa = std.testing.allocator;
16041604 var threaded: Io.Threaded = .init_single_threaded;
1605 const io = threaded.io();
1605 const io = threaded.ioBasic();
16061606 var discarding: Io.Writer.Discarding = .init(&.{});
16071607 var di: SelfInfo = .init;
16081608 defer di.deinit(gpa);
lib/std/fs.zig+1-1
......@@ -340,7 +340,7 @@ pub const OpenSelfExeError = Io.File.OpenSelfExeError;
340340pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
341341 if (native_os == .linux or native_os == .serenity or native_os == .windows) {
342342 var threaded: Io.Threaded = .init_single_threaded;
343 const io = threaded.io();
343 const io = threaded.ioBasic();
344344 return .adaptFromNewApi(try Io.File.openSelfExe(io, flags));
345345 }
346346 // Use of max_path_bytes here is valid as the resulting path is immediately
lib/std/fs/Dir.zig+11-11
......@@ -849,14 +849,14 @@ pub fn close(self: *Dir) void {
849849/// Deprecated in favor of `Io.Dir.openFile`.
850850pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
851851 var threaded: Io.Threaded = .init_single_threaded;
852 const io = threaded.io();
852 const io = threaded.ioBasic();
853853 return .adaptFromNewApi(try Io.Dir.openFile(self.adaptToNewApi(), io, sub_path, flags));
854854}
855855
856856/// Deprecated in favor of `Io.Dir.createFile`.
857857pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
858858 var threaded: Io.Threaded = .init_single_threaded;
859 const io = threaded.io();
859 const io = threaded.ioBasic();
860860 const new_file = try Io.Dir.createFile(self.adaptToNewApi(), io, sub_path, flags);
861861 return .adaptFromNewApi(new_file);
862862}
......@@ -867,7 +867,7 @@ pub const MakeError = Io.Dir.MakeError;
867867/// Deprecated in favor of `Io.Dir.makeDir`.
868868pub fn makeDir(self: Dir, sub_path: []const u8) MakeError!void {
869869 var threaded: Io.Threaded = .init_single_threaded;
870 const io = threaded.io();
870 const io = threaded.ioBasic();
871871 return Io.Dir.makeDir(.{ .handle = self.fd }, io, sub_path);
872872}
873873
......@@ -894,14 +894,14 @@ pub const MakePathError = Io.Dir.MakePathError;
894894/// Deprecated in favor of `Io.Dir.makePathStatus`.
895895pub fn makePathStatus(self: Dir, sub_path: []const u8) MakePathError!MakePathStatus {
896896 var threaded: Io.Threaded = .init_single_threaded;
897 const io = threaded.io();
897 const io = threaded.ioBasic();
898898 return Io.Dir.makePathStatus(.{ .handle = self.fd }, io, sub_path);
899899}
900900
901901/// Deprecated in favor of `Io.Dir.makeOpenPath`.
902902pub fn makeOpenPath(dir: Dir, sub_path: []const u8, options: OpenOptions) Io.Dir.MakeOpenPathError!Dir {
903903 var threaded: Io.Threaded = .init_single_threaded;
904 const io = threaded.io();
904 const io = threaded.ioBasic();
905905 return .adaptFromNewApi(try Io.Dir.makeOpenPath(dir.adaptToNewApi(), io, sub_path, options));
906906}
907907
......@@ -1070,7 +1070,7 @@ pub const OpenOptions = Io.Dir.OpenOptions;
10701070/// Deprecated in favor of `Io.Dir.openDir`.
10711071pub fn openDir(self: Dir, sub_path: []const u8, args: OpenOptions) OpenError!Dir {
10721072 var threaded: Io.Threaded = .init_single_threaded;
1073 const io = threaded.io();
1073 const io = threaded.ioBasic();
10741074 return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
10751075}
10761076
......@@ -1384,7 +1384,7 @@ pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
13841384/// Deprecated in favor of `Io.Dir.readFile`.
13851385pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 {
13861386 var threaded: Io.Threaded = .init_single_threaded;
1387 const io = threaded.io();
1387 const io = threaded.ioBasic();
13881388 return Io.Dir.readFile(.{ .handle = self.fd }, io, file_path, buffer);
13891389}
13901390
......@@ -1437,7 +1437,7 @@ pub fn readFileAllocOptions(
14371437 comptime sentinel: ?u8,
14381438) ReadFileAllocError!(if (sentinel) |s| [:s]align(alignment.toByteUnits()) u8 else []align(alignment.toByteUnits()) u8) {
14391439 var threaded: Io.Threaded = .init_single_threaded;
1440 const io = threaded.io();
1440 const io = threaded.ioBasic();
14411441
14421442 var file = try dir.openFile(sub_path, .{});
14431443 defer file.close();
......@@ -1892,7 +1892,7 @@ pub const AccessError = Io.Dir.AccessError;
18921892/// Deprecated in favor of `Io.Dir.access`.
18931893pub fn access(self: Dir, sub_path: []const u8, options: Io.Dir.AccessOptions) AccessError!void {
18941894 var threaded: Io.Threaded = .init_single_threaded;
1895 const io = threaded.io();
1895 const io = threaded.ioBasic();
18961896 return Io.Dir.access(self.adaptToNewApi(), io, sub_path, options);
18971897}
18981898
......@@ -1928,7 +1928,7 @@ pub fn copyFile(
19281928 options: CopyFileOptions,
19291929) CopyFileError!void {
19301930 var threaded: Io.Threaded = .init_single_threaded;
1931 const io = threaded.io();
1931 const io = threaded.ioBasic();
19321932
19331933 const file = try source_dir.openFile(source_path, .{});
19341934 var file_reader: File.Reader = .init(.{ .handle = file.handle }, io, &.{});
......@@ -1996,7 +1996,7 @@ pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError
19961996/// Deprecated in favor of `Io.Dir.statPath`.
19971997pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
19981998 var threaded: Io.Threaded = .init_single_threaded;
1999 const io = threaded.io();
1999 const io = threaded.ioBasic();
20002000 return Io.Dir.statPath(.{ .handle = self.fd }, io, sub_path, .{});
20012001}
20022002
lib/std/fs/File.zig+1-1
......@@ -313,7 +313,7 @@ pub const StatError = posix.FStatError;
313313/// Returns `Stat` containing basic information about the `File`.
314314pub fn stat(self: File) StatError!Stat {
315315 var threaded: Io.Threaded = .init_single_threaded;
316 const io = threaded.io();
316 const io = threaded.ioBasic();
317317 return Io.File.stat(.{ .handle = self.handle }, io);
318318}
319319
lib/std/process/Child.zig+1-1
......@@ -1089,7 +1089,7 @@ fn windowsCreateProcessPathExt(
10891089 // opening function knowing which implementation we are in. Here, we imitate
10901090 // that scenario.
10911091 var threaded: std.Io.Threaded = .init_single_threaded;
1092 const io = threaded.io();
1092 const io = threaded.ioBasic();
10931093
10941094 var dir = dir: {
10951095 // needs to be null-terminated
test/standalone/child_process/child.zig+10-3
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const Io = std.Io;
23
34// 42 is expected by parent; other values result in test failure
45var exit_code: u8 = 42;
......@@ -6,12 +7,17 @@ var exit_code: u8 = 42;
67pub fn main() !void {
78 var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
89 const arena = arena_state.allocator();
9 try run(arena);
10
11 var threaded: std.Io.Threaded = .init(arena);
12 defer threaded.deinit();
13 const io = threaded.io();
14
15 try run(arena, io);
1016 arena_state.deinit();
1117 std.process.exit(exit_code);
1218}
1319
14fn run(allocator: std.mem.Allocator) !void {
20fn run(allocator: std.mem.Allocator, io: Io) !void {
1521 var args = try std.process.argsWithAllocator(allocator);
1622 defer args.deinit();
1723 _ = args.next() orelse unreachable; // skip binary name
......@@ -33,7 +39,8 @@ fn run(allocator: std.mem.Allocator) !void {
3339 const hello_stdin = "hello from stdin";
3440 var buf: [hello_stdin.len]u8 = undefined;
3541 const stdin: std.fs.File = .stdin();
36 const n = try stdin.readAll(&buf);
42 var reader = stdin.reader(io, &.{});
43 const n = try reader.interface.readSliceShort(&buf);
3744 if (!std.mem.eql(u8, buf[0..n], hello_stdin)) {
3845 testError("stdin: '{s}'; want '{s}'", .{ buf[0..n], hello_stdin });
3946 }
test/standalone/simple/cat/main.zig+6-2
......@@ -9,6 +9,10 @@ pub fn main() !void {
99 defer arena_instance.deinit();
1010 const arena = arena_instance.allocator();
1111
12 var threaded: std.Io.Threaded = .init(arena);
13 defer threaded.deinit();
14 const io = threaded.io();
15
1216 const args = try std.process.argsAlloc(arena);
1317
1418 const exe = args[0];
......@@ -16,7 +20,7 @@ pub fn main() !void {
1620 var stdout_buffer: [4096]u8 = undefined;
1721 var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
1822 const stdout = &stdout_writer.interface;
19 var stdin_reader = fs.File.stdin().readerStreaming(&.{});
23 var stdin_reader = fs.File.stdin().readerStreaming(io, &.{});
2024
2125 const cwd = fs.cwd();
2226
......@@ -32,7 +36,7 @@ pub fn main() !void {
3236 defer file.close();
3337
3438 catted_anything = true;
35 var file_reader = file.reader(&.{});
39 var file_reader = file.reader(io, &.{});
3640 _ = try stdout.sendFileAll(&file_reader, .unlimited);
3741 try stdout.flush();
3842 }
tools/fetch_them_macos_headers.zig+7-5
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const Io = std.Io;
23const fs = std.fs;
34const mem = std.mem;
45const process = std.process;
......@@ -85,7 +86,7 @@ pub fn main() anyerror!void {
8586 } else try argv.append(arg);
8687 }
8788
88 var threaded: std.Io.Threaded = .init(gpa);
89 var threaded: Io.Threaded = .init(gpa);
8990 defer threaded.deinit();
9091 const io = threaded.io();
9192
......@@ -118,12 +119,13 @@ pub fn main() anyerror!void {
118119 .arch = arch,
119120 .os_ver = os_ver,
120121 };
121 try fetchTarget(allocator, argv.items, sysroot_path, target, version, tmp);
122 try fetchTarget(allocator, io, argv.items, sysroot_path, target, version, tmp);
122123 }
123124}
124125
125126fn fetchTarget(
126127 arena: Allocator,
128 io: Io,
127129 args: []const []const u8,
128130 sysroot: []const u8,
129131 target: Target,
......@@ -194,7 +196,7 @@ fn fetchTarget(
194196 var dirs = std.StringHashMap(fs.Dir).init(arena);
195197 try dirs.putNoClobber(".", dest_dir);
196198
197 var headers_list_file_reader = headers_list_file.reader(&.{});
199 var headers_list_file_reader = headers_list_file.reader(io, &.{});
198200 const headers_list_str = try headers_list_file_reader.interface.allocRemaining(arena, .unlimited);
199201 const prefix = "/usr/include";
200202
......@@ -267,8 +269,8 @@ const Version = struct {
267269
268270 pub fn format(
269271 v: Version,
270 writer: *std.Io.Writer,
271 ) std.Io.Writer.Error!void {
272 writer: *Io.Writer,
273 ) Io.Writer.Error!void {
272274 try writer.print("{d}.{d}.{d}", .{ v.major, v.minor, v.patch });
273275 }
274276};
tools/gen_macos_headers_c.zig+1-1
......@@ -73,7 +73,7 @@ fn findHeaders(
7373 switch (entry.kind) {
7474 .directory => {
7575 const path = try std.fs.path.join(arena, &.{ prefix, entry.name });
76 var subdir = try dir.openDir(entry.name, .{ .no_follow = true });
76 var subdir = try dir.openDir(entry.name, .{ .follow_symlinks = false });
7777 defer subdir.close();
7878 try findHeaders(arena, subdir, path, paths);
7979 },