diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-08 21:57:46 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:08 -0800 |
| commit | 90f7259ef17e8e07c2c3f71bea65d103cfe52c07 (patch) | |
| tree | c7d4025d746c265dd5907b81295b86d6f67eb80c /lib/std | |
| parent | bee8005fe6817ade9191de0493888b14cdbcac31 (diff) | |
| download | zig-90f7259ef17e8e07c2c3f71bea65d103cfe52c07.tar.gz zig-90f7259ef17e8e07c2c3f71bea65d103cfe52c07.zip | |
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.
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/Io/Threaded.zig | 20 | ||||
| -rw-r--r-- | lib/std/Progress.zig | 71 | ||||
| -rw-r--r-- | lib/std/debug.zig | 11 |
3 files changed, 62 insertions, 40 deletions
diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 945e47d5fd..459440f5a6 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -771,6 +771,7 @@ pub fn io(t: *Threaded) Io { .windows => netWriteWindows, else => netWritePosix, }, + .netWriteFile = netWriteFile, .netSend = switch (native_os) { .windows => netSendWindows, else => netSendPosix, @@ -872,6 +873,7 @@ pub fn ioBasic(t: *Threaded) Io { .netClose = netCloseUnavailable, .netRead = netReadUnavailable, .netWrite = netWriteUnavailable, + .netWriteFile = netWriteFileUnavailable, .netSend = netSendUnavailable, .netReceive = netReceiveUnavailable, .netInterfaceNameResolve = netInterfaceNameResolveUnavailable, @@ -6782,7 +6784,7 @@ fn netWriteFile( header: []const u8, file_reader: *File.Reader, limit: Io.Limit, -) net.Stream.WriteFileError!usize { +) net.Stream.Writer.WriteFileError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); _ = t; _ = socket_handle; @@ -6792,6 +6794,22 @@ fn netWriteFile( return error.Unimplemented; // TODO } +fn netWriteFileUnavailable( + userdata: ?*anyopaque, + socket_handle: net.Socket.Handle, + header: []const u8, + file_reader: *File.Reader, + limit: Io.Limit, +) net.Stream.Writer.WriteFileError!usize { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; + _ = socket_handle; + _ = header; + _ = file_reader; + _ = limit; + return error.NetworkDown; +} + fn fileWriteFilePositional( userdata: ?*anyopaque, file: File, diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index d8b22c2db0..a839de8c4f 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -458,13 +458,16 @@ pub fn start(options: Options) Node { if (noop_impl) return Node.none; + const io = static_threaded_io.io(); + if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| { global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{ - @as(posix.fd_t, switch (@typeInfo(posix.fd_t)) { + io, + @as(Io.File, .{ .handle = switch (@typeInfo(posix.fd_t)) { .int => ipc_fd, .pointer => @ptrFromInt(ipc_fd), else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)), - }), + } }), }) catch |err| { std.log.warn("failed to spawn IPC thread for communicating progress to parent: {s}", .{@errorName(err)}); return Node.none; @@ -476,9 +479,9 @@ pub fn start(options: Options) Node { } const stderr: Io.File = .stderr(); global_progress.terminal = stderr; - if (stderr.enableAnsiEscapeCodes()) |_| { + if (stderr.enableAnsiEscapeCodes(io)) |_| { global_progress.terminal_mode = .ansi_escape_codes; - } else |_| if (is_windows and stderr.isTty()) { + } else |_| if (is_windows and stderr.isTty(io)) { global_progress.terminal_mode = TerminalMode{ .windows_api = .{ .code_page = windows.kernel32.GetConsoleOutputCP(), } }; @@ -499,8 +502,8 @@ pub fn start(options: Options) Node { if (switch (global_progress.terminal_mode) { .off => unreachable, // handled a few lines above - .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{}), - .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{}) else unreachable, + .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{io}), + .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{io}) else unreachable, }) |thread| { global_progress.update_thread = thread; } else |err| { @@ -531,7 +534,7 @@ fn wait(timeout_ns: u64) bool { return resize_flag or (global_progress.cols == 0); } -fn updateThreadRun() void { +fn updateThreadRun(io: Io) void { // Store this data in the thread so that it does not need to be part of the // linker data of the main executable. var serialized_buffer: Serialized.Buffer = undefined; @@ -544,7 +547,7 @@ fn updateThreadRun() void { const buffer, _ = computeRedraw(&serialized_buffer); if (stderr_mutex.tryLock()) { defer stderr_mutex.unlock(); - write(buffer) catch return; + write(io, buffer) catch return; global_progress.need_clear = true; } } @@ -555,7 +558,7 @@ fn updateThreadRun() void { if (@atomicLoad(bool, &global_progress.done, .monotonic)) { stderr_mutex.lock(); defer stderr_mutex.unlock(); - return clearWrittenWithEscapeCodes() catch {}; + return clearWrittenWithEscapeCodes(io) catch {}; } maybeUpdateSize(resize_flag); @@ -563,7 +566,7 @@ fn updateThreadRun() void { const buffer, _ = computeRedraw(&serialized_buffer); if (stderr_mutex.tryLock()) { defer stderr_mutex.unlock(); - write(buffer) catch return; + write(io, buffer) catch return; global_progress.need_clear = true; } } @@ -577,7 +580,7 @@ fn windowsApiWriteMarker() void { _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null); } -fn windowsApiUpdateThreadRun() void { +fn windowsApiUpdateThreadRun(io: Io) void { var serialized_buffer: Serialized.Buffer = undefined; { @@ -589,7 +592,7 @@ fn windowsApiUpdateThreadRun() void { if (stderr_mutex.tryLock()) { defer stderr_mutex.unlock(); windowsApiWriteMarker(); - write(buffer) catch return; + write(io, buffer) catch return; global_progress.need_clear = true; windowsApiMoveToMarker(nl_n) catch return; } @@ -611,7 +614,7 @@ fn windowsApiUpdateThreadRun() void { defer stderr_mutex.unlock(); clearWrittenWindowsApi() catch return; windowsApiWriteMarker(); - write(buffer) catch return; + write(io, buffer) catch return; global_progress.need_clear = true; windowsApiMoveToMarker(nl_n) catch return; } @@ -624,8 +627,9 @@ fn windowsApiUpdateThreadRun() void { /// /// The lock is recursive; the same thread may hold the lock multiple times. pub fn lockStdErr() void { + const io = stderr_file_writer.io; stderr_mutex.lock(); - clearWrittenWithEscapeCodes() catch {}; + clearWrittenWithEscapeCodes(io) catch {}; } pub fn unlockStdErr() void { @@ -636,10 +640,12 @@ pub fn unlockStdErr() void { const stderr_writer: *Writer = &stderr_file_writer.interface; /// Protected by `stderr_mutex`. var stderr_file_writer: Io.File.Writer = .{ + .io = static_threaded_io.io(), .interface = Io.File.Writer.initInterface(&.{}), .file = if (is_windows) undefined else .stderr(), .mode = .streaming, }; +var static_threaded_io: Io.Threaded = .init_single_threaded; /// Allows the caller to freely write to the returned `Writer`, /// initialized with `buffer`, until `unlockStderrWriter` is called. @@ -647,9 +653,10 @@ var stderr_file_writer: Io.File.Writer = .{ /// During the lock, any `std.Progress` information is cleared from the terminal. /// /// The lock is recursive; the same thread may hold the lock multiple times. -pub fn lockStderrWriter(buffer: []u8) *Writer { +pub fn lockStderrWriter(buffer: []u8) *Io.Writer { + const io = stderr_file_writer.io; stderr_mutex.lock(); - clearWrittenWithEscapeCodes() catch {}; + clearWrittenWithEscapeCodes(io) catch {}; if (is_windows) stderr_file_writer.file = .stderr(); stderr_writer.flush() catch {}; stderr_writer.buffer = buffer; @@ -663,7 +670,7 @@ pub fn unlockStderrWriter() void { stderr_mutex.unlock(); } -fn ipcThreadRun(fd: posix.fd_t) anyerror!void { +fn ipcThreadRun(io: Io, file: Io.File) anyerror!void { // Store this data in the thread so that it does not need to be part of the // linker data of the main executable. var serialized_buffer: Serialized.Buffer = undefined; @@ -675,7 +682,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { return; const serialized = serialize(&serialized_buffer); - writeIpc(fd, serialized) catch |err| switch (err) { + writeIpc(io, file, serialized) catch |err| switch (err) { error.BrokenPipe => return, }; } @@ -687,7 +694,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { return; const serialized = serialize(&serialized_buffer); - writeIpc(fd, serialized) catch |err| switch (err) { + writeIpc(io, file, serialized) catch |err| switch (err) { error.BrokenPipe => return, }; } @@ -786,11 +793,11 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { } } -fn clearWrittenWithEscapeCodes() anyerror!void { +fn clearWrittenWithEscapeCodes(io: Io) anyerror!void { if (noop_impl or !global_progress.need_clear) return; global_progress.need_clear = false; - try write(clear ++ progress_remove); + try write(io, clear ++ progress_remove); } /// U+25BA or ► @@ -1417,13 +1424,13 @@ fn withinRowLimit(p: *Progress, nl_n: usize) bool { return nl_n + 2 < p.rows; } -fn write(buf: []const u8) anyerror!void { - try global_progress.terminal.writeAll(buf); +fn write(io: Io, buf: []const u8) anyerror!void { + try global_progress.terminal.writeStreamingAll(io, buf); } var remaining_write_trash_bytes: usize = 0; -fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { +fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!void { // Byteswap if necessary to ensure little endian over the pipe. This is // needed because the parent or child process might be running in qemu. 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 { const storage = std.mem.sliceAsBytes(serialized.storage); const parents = std.mem.sliceAsBytes(serialized.parents); - var vecs: [3]posix.iovec_const = .{ - .{ .base = header.ptr, .len = header.len }, - .{ .base = storage.ptr, .len = storage.len }, - .{ .base = parents.ptr, .len = parents.len }, - }; + var vecs: [3][]const u8 = .{ header, storage, parents }; // Ensures the packet can fit in the pipe buffer. 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 { // We do this in a separate write call to give a better chance for the // writev below to be in a single packet. const n = @min(parents.len, remaining_write_trash_bytes); - if (posix.write(fd, parents[0..n])) |written| { + if (io.vtable.fileWriteStreaming(io.userdata, file, &.{}, &.{parents[0..n]}, 1)) |written| { remaining_write_trash_bytes -= written; continue; } else |err| switch (err) { @@ -1464,7 +1467,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { // If this write would block we do not want to keep trying, but we need to // know if a partial message was written. - if (writevNonblock(fd, &vecs)) |written| { + if (writevNonblock(io, file, &vecs)) |written| { const total = header.len + storage.len + parents.len; if (written < total) { remaining_write_trash_bytes = total - written; @@ -1479,7 +1482,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { } } -fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usize { +fn writevNonblock(io: Io, file: Io.File, iov: [][]const u8) Io.File.Writer.Error!usize { var iov_index: usize = 0; var written: usize = 0; var total_written: usize = 0; @@ -1488,9 +1491,9 @@ fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usi written >= iov[iov_index].len else return total_written) : (iov_index += 1) written -= iov[iov_index].len; - iov[iov_index].base += written; + iov[iov_index].ptr += written; iov[iov_index].len -= written; - written = try posix.writev(fd, iov[iov_index..]); + written = try io.vtable.fileWriteStreaming(io.userdata, file, &.{}, iov, 1); if (written == 0) return total_written; total_written += written; } diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 8f1cd50e8e..6ffa254271 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -286,13 +286,12 @@ pub fn unlockStdErr() void { pub fn lockStderrWriter(buffer: []u8) struct { *Writer, tty.Config } { const global = struct { var conf: ?tty.Config = null; - var single_threaded_io: Io.Threaded = .init_single_threaded; }; - const io = global.single_threaded_io.io(); const w = std.Progress.lockStderrWriter(buffer); + const file_writer: *File.Writer = @fieldParentPtr("interface", w); // The stderr lock also locks access to `global.conf`. if (global.conf == null) { - global.conf = .detect(io, .stderr()); + global.conf = .detect(file_writer.io, .stderr()); } return .{ w, global.conf.? }; } @@ -619,13 +618,15 @@ pub const StackUnwindOptions = struct { /// /// See `writeCurrentStackTrace` to immediately print the trace instead of capturing it. pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace { - var threaded: Io.Threaded = .init_single_threaded; - const io = threaded.ioBasic(); const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} }; if (!std.options.allow_stack_tracing) return empty_trace; var it: StackIterator = .init(options.context); defer it.deinit(); if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace; + + var threaded: Io.Threaded = .init_single_threaded; + const io = threaded.ioBasic(); + var total_frames: usize = 0; var index: usize = 0; var wait_for = options.first_address; |
