authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-08 21:57:46-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:08-08:00
log90f7259ef17e8e07c2c3f71bea65d103cfe52c07
treec7d4025d746c265dd5907b81295b86d6f67eb80c
parentbee8005fe6817ade9191de0493888b14cdbcac31

std.Progress: use a global static Io instance

This decision should be audited and discussed. Some factors: * Passing an Io instance into start. * Avoiding reference to global static instance if it won't be used, so that it doesn't bloat the executable. * Being able to use std.debug.print, and related functionality when debugging std.Io instances and std.Progress.

3 files changed, 62 insertions(+), 40 deletions(-)

lib/std/Io/Threaded.zig+19-1
...@@ -771,6 +771,7 @@ pub fn io(t: *Threaded) Io {...@@ -771,6 +771,7 @@ pub fn io(t: *Threaded) Io {
771 .windows => netWriteWindows,771 .windows => netWriteWindows,
772 else => netWritePosix,772 else => netWritePosix,
773 },773 },
774 .netWriteFile = netWriteFile,
774 .netSend = switch (native_os) {775 .netSend = switch (native_os) {
775 .windows => netSendWindows,776 .windows => netSendWindows,
776 else => netSendPosix,777 else => netSendPosix,
...@@ -872,6 +873,7 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -872,6 +873,7 @@ pub fn ioBasic(t: *Threaded) Io {
872 .netClose = netCloseUnavailable,873 .netClose = netCloseUnavailable,
873 .netRead = netReadUnavailable,874 .netRead = netReadUnavailable,
874 .netWrite = netWriteUnavailable,875 .netWrite = netWriteUnavailable,
876 .netWriteFile = netWriteFileUnavailable,
875 .netSend = netSendUnavailable,877 .netSend = netSendUnavailable,
876 .netReceive = netReceiveUnavailable,878 .netReceive = netReceiveUnavailable,
877 .netInterfaceNameResolve = netInterfaceNameResolveUnavailable,879 .netInterfaceNameResolve = netInterfaceNameResolveUnavailable,
...@@ -6782,7 +6784,7 @@ fn netWriteFile(...@@ -6782,7 +6784,7 @@ fn netWriteFile(
6782 header: []const u8,6784 header: []const u8,
6783 file_reader: *File.Reader,6785 file_reader: *File.Reader,
6784 limit: Io.Limit,6786 limit: Io.Limit,
6785) net.Stream.WriteFileError!usize {6787) net.Stream.Writer.WriteFileError!usize {
6786 const t: *Threaded = @ptrCast(@alignCast(userdata));6788 const t: *Threaded = @ptrCast(@alignCast(userdata));
6787 _ = t;6789 _ = t;
6788 _ = socket_handle;6790 _ = socket_handle;
...@@ -6792,6 +6794,22 @@ fn netWriteFile(...@@ -6792,6 +6794,22 @@ fn netWriteFile(
6792 return error.Unimplemented; // TODO6794 return error.Unimplemented; // TODO
6793}6795}
67946796
6797fn netWriteFileUnavailable(
6798 userdata: ?*anyopaque,
6799 socket_handle: net.Socket.Handle,
6800 header: []const u8,
6801 file_reader: *File.Reader,
6802 limit: Io.Limit,
6803) net.Stream.Writer.WriteFileError!usize {
6804 const t: *Threaded = @ptrCast(@alignCast(userdata));
6805 _ = t;
6806 _ = socket_handle;
6807 _ = header;
6808 _ = file_reader;
6809 _ = limit;
6810 return error.NetworkDown;
6811}
6812
6795fn fileWriteFilePositional(6813fn fileWriteFilePositional(
6796 userdata: ?*anyopaque,6814 userdata: ?*anyopaque,
6797 file: File,6815 file: File,
lib/std/Progress.zig+37-34
...@@ -458,13 +458,16 @@ pub fn start(options: Options) Node {...@@ -458,13 +458,16 @@ pub fn start(options: Options) Node {
458 if (noop_impl)458 if (noop_impl)
459 return Node.none;459 return Node.none;
460460
461 const io = static_threaded_io.io();
462
461 if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| {463 if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| {
462 global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{464 global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{
463 @as(posix.fd_t, switch (@typeInfo(posix.fd_t)) {465 io,
466 @as(Io.File, .{ .handle = switch (@typeInfo(posix.fd_t)) {
464 .int => ipc_fd,467 .int => ipc_fd,
465 .pointer => @ptrFromInt(ipc_fd),468 .pointer => @ptrFromInt(ipc_fd),
466 else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)),469 else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)),
467 }),470 } }),
468 }) catch |err| {471 }) catch |err| {
469 std.log.warn("failed to spawn IPC thread for communicating progress to parent: {s}", .{@errorName(err)});472 std.log.warn("failed to spawn IPC thread for communicating progress to parent: {s}", .{@errorName(err)});
470 return Node.none;473 return Node.none;
...@@ -476,9 +479,9 @@ pub fn start(options: Options) Node {...@@ -476,9 +479,9 @@ pub fn start(options: Options) Node {
476 }479 }
477 const stderr: Io.File = .stderr();480 const stderr: Io.File = .stderr();
478 global_progress.terminal = stderr;481 global_progress.terminal = stderr;
479 if (stderr.enableAnsiEscapeCodes()) |_| {482 if (stderr.enableAnsiEscapeCodes(io)) |_| {
480 global_progress.terminal_mode = .ansi_escape_codes;483 global_progress.terminal_mode = .ansi_escape_codes;
481 } else |_| if (is_windows and stderr.isTty()) {484 } else |_| if (is_windows and stderr.isTty(io)) {
482 global_progress.terminal_mode = TerminalMode{ .windows_api = .{485 global_progress.terminal_mode = TerminalMode{ .windows_api = .{
483 .code_page = windows.kernel32.GetConsoleOutputCP(),486 .code_page = windows.kernel32.GetConsoleOutputCP(),
484 } };487 } };
...@@ -499,8 +502,8 @@ pub fn start(options: Options) Node {...@@ -499,8 +502,8 @@ pub fn start(options: Options) Node {
499502
500 if (switch (global_progress.terminal_mode) {503 if (switch (global_progress.terminal_mode) {
501 .off => unreachable, // handled a few lines above504 .off => unreachable, // handled a few lines above
502 .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{}),505 .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{io}),
503 .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{}) else unreachable,506 .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{io}) else unreachable,
504 }) |thread| {507 }) |thread| {
505 global_progress.update_thread = thread;508 global_progress.update_thread = thread;
506 } else |err| {509 } else |err| {
...@@ -531,7 +534,7 @@ fn wait(timeout_ns: u64) bool {...@@ -531,7 +534,7 @@ fn wait(timeout_ns: u64) bool {
531 return resize_flag or (global_progress.cols == 0);534 return resize_flag or (global_progress.cols == 0);
532}535}
533536
534fn updateThreadRun() void {537fn updateThreadRun(io: Io) void {
535 // Store this data in the thread so that it does not need to be part of the538 // Store this data in the thread so that it does not need to be part of the
536 // linker data of the main executable.539 // linker data of the main executable.
537 var serialized_buffer: Serialized.Buffer = undefined;540 var serialized_buffer: Serialized.Buffer = undefined;
...@@ -544,7 +547,7 @@ fn updateThreadRun() void {...@@ -544,7 +547,7 @@ fn updateThreadRun() void {
544 const buffer, _ = computeRedraw(&serialized_buffer);547 const buffer, _ = computeRedraw(&serialized_buffer);
545 if (stderr_mutex.tryLock()) {548 if (stderr_mutex.tryLock()) {
546 defer stderr_mutex.unlock();549 defer stderr_mutex.unlock();
547 write(buffer) catch return;550 write(io, buffer) catch return;
548 global_progress.need_clear = true;551 global_progress.need_clear = true;
549 }552 }
550 }553 }
...@@ -555,7 +558,7 @@ fn updateThreadRun() void {...@@ -555,7 +558,7 @@ fn updateThreadRun() void {
555 if (@atomicLoad(bool, &global_progress.done, .monotonic)) {558 if (@atomicLoad(bool, &global_progress.done, .monotonic)) {
556 stderr_mutex.lock();559 stderr_mutex.lock();
557 defer stderr_mutex.unlock();560 defer stderr_mutex.unlock();
558 return clearWrittenWithEscapeCodes() catch {};561 return clearWrittenWithEscapeCodes(io) catch {};
559 }562 }
560563
561 maybeUpdateSize(resize_flag);564 maybeUpdateSize(resize_flag);
...@@ -563,7 +566,7 @@ fn updateThreadRun() void {...@@ -563,7 +566,7 @@ fn updateThreadRun() void {
563 const buffer, _ = computeRedraw(&serialized_buffer);566 const buffer, _ = computeRedraw(&serialized_buffer);
564 if (stderr_mutex.tryLock()) {567 if (stderr_mutex.tryLock()) {
565 defer stderr_mutex.unlock();568 defer stderr_mutex.unlock();
566 write(buffer) catch return;569 write(io, buffer) catch return;
567 global_progress.need_clear = true;570 global_progress.need_clear = true;
568 }571 }
569 }572 }
...@@ -577,7 +580,7 @@ fn windowsApiWriteMarker() void {...@@ -577,7 +580,7 @@ fn windowsApiWriteMarker() void {
577 _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null);580 _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null);
578}581}
579582
580fn windowsApiUpdateThreadRun() void {583fn windowsApiUpdateThreadRun(io: Io) void {
581 var serialized_buffer: Serialized.Buffer = undefined;584 var serialized_buffer: Serialized.Buffer = undefined;
582585
583 {586 {
...@@ -589,7 +592,7 @@ fn windowsApiUpdateThreadRun() void {...@@ -589,7 +592,7 @@ fn windowsApiUpdateThreadRun() void {
589 if (stderr_mutex.tryLock()) {592 if (stderr_mutex.tryLock()) {
590 defer stderr_mutex.unlock();593 defer stderr_mutex.unlock();
591 windowsApiWriteMarker();594 windowsApiWriteMarker();
592 write(buffer) catch return;595 write(io, buffer) catch return;
593 global_progress.need_clear = true;596 global_progress.need_clear = true;
594 windowsApiMoveToMarker(nl_n) catch return;597 windowsApiMoveToMarker(nl_n) catch return;
595 }598 }
...@@ -611,7 +614,7 @@ fn windowsApiUpdateThreadRun() void {...@@ -611,7 +614,7 @@ fn windowsApiUpdateThreadRun() void {
611 defer stderr_mutex.unlock();614 defer stderr_mutex.unlock();
612 clearWrittenWindowsApi() catch return;615 clearWrittenWindowsApi() catch return;
613 windowsApiWriteMarker();616 windowsApiWriteMarker();
614 write(buffer) catch return;617 write(io, buffer) catch return;
615 global_progress.need_clear = true;618 global_progress.need_clear = true;
616 windowsApiMoveToMarker(nl_n) catch return;619 windowsApiMoveToMarker(nl_n) catch return;
617 }620 }
...@@ -624,8 +627,9 @@ fn windowsApiUpdateThreadRun() void {...@@ -624,8 +627,9 @@ fn windowsApiUpdateThreadRun() void {
624///627///
625/// The lock is recursive; the same thread may hold the lock multiple times.628/// The lock is recursive; the same thread may hold the lock multiple times.
626pub fn lockStdErr() void {629pub fn lockStdErr() void {
630 const io = stderr_file_writer.io;
627 stderr_mutex.lock();631 stderr_mutex.lock();
628 clearWrittenWithEscapeCodes() catch {};632 clearWrittenWithEscapeCodes(io) catch {};
629}633}
630634
631pub fn unlockStdErr() void {635pub fn unlockStdErr() void {
...@@ -636,10 +640,12 @@ pub fn unlockStdErr() void {...@@ -636,10 +640,12 @@ pub fn unlockStdErr() void {
636const stderr_writer: *Writer = &stderr_file_writer.interface;640const stderr_writer: *Writer = &stderr_file_writer.interface;
637/// Protected by `stderr_mutex`.641/// Protected by `stderr_mutex`.
638var stderr_file_writer: Io.File.Writer = .{642var stderr_file_writer: Io.File.Writer = .{
643 .io = static_threaded_io.io(),
639 .interface = Io.File.Writer.initInterface(&.{}),644 .interface = Io.File.Writer.initInterface(&.{}),
640 .file = if (is_windows) undefined else .stderr(),645 .file = if (is_windows) undefined else .stderr(),
641 .mode = .streaming,646 .mode = .streaming,
642};647};
648var static_threaded_io: Io.Threaded = .init_single_threaded;
643649
644/// Allows the caller to freely write to the returned `Writer`,650/// Allows the caller to freely write to the returned `Writer`,
645/// initialized with `buffer`, until `unlockStderrWriter` is called.651/// initialized with `buffer`, until `unlockStderrWriter` is called.
...@@ -647,9 +653,10 @@ var stderr_file_writer: Io.File.Writer = .{...@@ -647,9 +653,10 @@ var stderr_file_writer: Io.File.Writer = .{
647/// During the lock, any `std.Progress` information is cleared from the terminal.653/// During the lock, any `std.Progress` information is cleared from the terminal.
648///654///
649/// The lock is recursive; the same thread may hold the lock multiple times.655/// The lock is recursive; the same thread may hold the lock multiple times.
650pub fn lockStderrWriter(buffer: []u8) *Writer {656pub fn lockStderrWriter(buffer: []u8) *Io.Writer {
657 const io = stderr_file_writer.io;
651 stderr_mutex.lock();658 stderr_mutex.lock();
652 clearWrittenWithEscapeCodes() catch {};659 clearWrittenWithEscapeCodes(io) catch {};
653 if (is_windows) stderr_file_writer.file = .stderr();660 if (is_windows) stderr_file_writer.file = .stderr();
654 stderr_writer.flush() catch {};661 stderr_writer.flush() catch {};
655 stderr_writer.buffer = buffer;662 stderr_writer.buffer = buffer;
...@@ -663,7 +670,7 @@ pub fn unlockStderrWriter() void {...@@ -663,7 +670,7 @@ pub fn unlockStderrWriter() void {
663 stderr_mutex.unlock();670 stderr_mutex.unlock();
664}671}
665672
666fn ipcThreadRun(fd: posix.fd_t) anyerror!void {673fn ipcThreadRun(io: Io, file: Io.File) anyerror!void {
667 // Store this data in the thread so that it does not need to be part of the674 // Store this data in the thread so that it does not need to be part of the
668 // linker data of the main executable.675 // linker data of the main executable.
669 var serialized_buffer: Serialized.Buffer = undefined;676 var serialized_buffer: Serialized.Buffer = undefined;
...@@ -675,7 +682,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void {...@@ -675,7 +682,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void {
675 return;682 return;
676683
677 const serialized = serialize(&serialized_buffer);684 const serialized = serialize(&serialized_buffer);
678 writeIpc(fd, serialized) catch |err| switch (err) {685 writeIpc(io, file, serialized) catch |err| switch (err) {
679 error.BrokenPipe => return,686 error.BrokenPipe => return,
680 };687 };
681 }688 }
...@@ -687,7 +694,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void {...@@ -687,7 +694,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void {
687 return;694 return;
688695
689 const serialized = serialize(&serialized_buffer);696 const serialized = serialize(&serialized_buffer);
690 writeIpc(fd, serialized) catch |err| switch (err) {697 writeIpc(io, file, serialized) catch |err| switch (err) {
691 error.BrokenPipe => return,698 error.BrokenPipe => return,
692 };699 };
693 }700 }
...@@ -786,11 +793,11 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize {...@@ -786,11 +793,11 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize {
786 }793 }
787}794}
788795
789fn clearWrittenWithEscapeCodes() anyerror!void {796fn clearWrittenWithEscapeCodes(io: Io) anyerror!void {
790 if (noop_impl or !global_progress.need_clear) return;797 if (noop_impl or !global_progress.need_clear) return;
791798
792 global_progress.need_clear = false;799 global_progress.need_clear = false;
793 try write(clear ++ progress_remove);800 try write(io, clear ++ progress_remove);
794}801}
795802
796/// U+25BA or ►803/// U+25BA or ►
...@@ -1417,13 +1424,13 @@ fn withinRowLimit(p: *Progress, nl_n: usize) bool {...@@ -1417,13 +1424,13 @@ fn withinRowLimit(p: *Progress, nl_n: usize) bool {
1417 return nl_n + 2 < p.rows;1424 return nl_n + 2 < p.rows;
1418}1425}
14191426
1420fn write(buf: []const u8) anyerror!void {1427fn write(io: Io, buf: []const u8) anyerror!void {
1421 try global_progress.terminal.writeAll(buf);1428 try global_progress.terminal.writeStreamingAll(io, buf);
1422}1429}
14231430
1424var remaining_write_trash_bytes: usize = 0;1431var remaining_write_trash_bytes: usize = 0;
14251432
1426fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {1433fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!void {
1427 // Byteswap if necessary to ensure little endian over the pipe. This is1434 // Byteswap if necessary to ensure little endian over the pipe. This is
1428 // needed because the parent or child process might be running in qemu.1435 // needed because the parent or child process might be running in qemu.
1429 if (is_big_endian) for (serialized.storage) |*s| s.byteSwap();1436 if (is_big_endian) for (serialized.storage) |*s| s.byteSwap();
...@@ -1434,11 +1441,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {...@@ -1434,11 +1441,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {
1434 const storage = std.mem.sliceAsBytes(serialized.storage);1441 const storage = std.mem.sliceAsBytes(serialized.storage);
1435 const parents = std.mem.sliceAsBytes(serialized.parents);1442 const parents = std.mem.sliceAsBytes(serialized.parents);
14361443
1437 var vecs: [3]posix.iovec_const = .{1444 var vecs: [3][]const u8 = .{ header, storage, parents };
1438 .{ .base = header.ptr, .len = header.len },
1439 .{ .base = storage.ptr, .len = storage.len },
1440 .{ .base = parents.ptr, .len = parents.len },
1441 };
14421445
1443 // Ensures the packet can fit in the pipe buffer.1446 // Ensures the packet can fit in the pipe buffer.
1444 const upper_bound_msg_len = 1 + node_storage_buffer_len * @sizeOf(Node.Storage) +1447 const upper_bound_msg_len = 1 + node_storage_buffer_len * @sizeOf(Node.Storage) +
...@@ -1449,7 +1452,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {...@@ -1449,7 +1452,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {
1449 // We do this in a separate write call to give a better chance for the1452 // We do this in a separate write call to give a better chance for the
1450 // writev below to be in a single packet.1453 // writev below to be in a single packet.
1451 const n = @min(parents.len, remaining_write_trash_bytes);1454 const n = @min(parents.len, remaining_write_trash_bytes);
1452 if (posix.write(fd, parents[0..n])) |written| {1455 if (io.vtable.fileWriteStreaming(io.userdata, file, &.{}, &.{parents[0..n]}, 1)) |written| {
1453 remaining_write_trash_bytes -= written;1456 remaining_write_trash_bytes -= written;
1454 continue;1457 continue;
1455 } else |err| switch (err) {1458 } else |err| switch (err) {
...@@ -1464,7 +1467,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {...@@ -1464,7 +1467,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {
14641467
1465 // If this write would block we do not want to keep trying, but we need to1468 // If this write would block we do not want to keep trying, but we need to
1466 // know if a partial message was written.1469 // know if a partial message was written.
1467 if (writevNonblock(fd, &vecs)) |written| {1470 if (writevNonblock(io, file, &vecs)) |written| {
1468 const total = header.len + storage.len + parents.len;1471 const total = header.len + storage.len + parents.len;
1469 if (written < total) {1472 if (written < total) {
1470 remaining_write_trash_bytes = total - written;1473 remaining_write_trash_bytes = total - written;
...@@ -1479,7 +1482,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {...@@ -1479,7 +1482,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {
1479 }1482 }
1480}1483}
14811484
1482fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usize {1485fn writevNonblock(io: Io, file: Io.File, iov: [][]const u8) Io.File.Writer.Error!usize {
1483 var iov_index: usize = 0;1486 var iov_index: usize = 0;
1484 var written: usize = 0;1487 var written: usize = 0;
1485 var total_written: usize = 0;1488 var total_written: usize = 0;
...@@ -1488,9 +1491,9 @@ fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usi...@@ -1488,9 +1491,9 @@ fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usi
1488 written >= iov[iov_index].len1491 written >= iov[iov_index].len
1489 else1492 else
1490 return total_written) : (iov_index += 1) written -= iov[iov_index].len;1493 return total_written) : (iov_index += 1) written -= iov[iov_index].len;
1491 iov[iov_index].base += written;1494 iov[iov_index].ptr += written;
1492 iov[iov_index].len -= written;1495 iov[iov_index].len -= written;
1493 written = try posix.writev(fd, iov[iov_index..]);1496 written = try io.vtable.fileWriteStreaming(io.userdata, file, &.{}, iov, 1);
1494 if (written == 0) return total_written;1497 if (written == 0) return total_written;
1495 total_written += written;1498 total_written += written;
1496 }1499 }
lib/std/debug.zig+6-5
...@@ -286,13 +286,12 @@ pub fn unlockStdErr() void {...@@ -286,13 +286,12 @@ pub fn unlockStdErr() void {
286pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } {286pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } {
287 const global = struct {287 const global = struct {
288 var conf: ?tty.Config = null;288 var conf: ?tty.Config = null;
289 var single_threaded_io: Io.Threaded = .init_single_threaded;
290 };289 };
291 const io = global.single_threaded_io.io();
292 const w = std.Progress.lockStderrWriter(buffer);290 const w = std.Progress.lockStderrWriter(buffer);
291 const file_writer: *File.Writer = @fieldParentPtr("interface", w);
293 // The stderr lock also locks access to `global.conf`.292 // The stderr lock also locks access to `global.conf`.
294 if (global.conf == null) {293 if (global.conf == null) {
295 global.conf = .detect(io, .stderr());294 global.conf = .detect(file_writer.io, .stderr());
296 }295 }
297 return .{ w, global.conf.? };296 return .{ w, global.conf.? };
298}297}
...@@ -619,13 +618,15 @@ pub const StackUnwindOptions = struct {...@@ -619,13 +618,15 @@ pub const StackUnwindOptions = struct {
619///618///
620/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.619/// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it.
621pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {620pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {
622 var threaded: Io.Threaded = .init_single_threaded;
623 const io = threaded.ioBasic();
624 const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} };621 const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} };
625 if (!std.options.allow_stack_tracing) return empty_trace;622 if (!std.options.allow_stack_tracing) return empty_trace;
626 var it: StackIterator = .init(options.context);623 var it: StackIterator = .init(options.context);
627 defer it.deinit();624 defer it.deinit();
628 if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace;625 if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace;
626
627 var threaded: Io.Threaded = .init_single_threaded;
628 const io = threaded.ioBasic();
629
629 var total_frames: usize = 0;630 var total_frames: usize = 0;
630 var index: usize = 0;631 var index: usize = 0;
631 var wait_for = options.first_address;632 var wait_for = options.first_address;