diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 77eedccb743b5f836804e7a73c94c17fd5672e5e..1e73f0e012efb47e7c6a706538fc0515a986f236 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -713,8 +713,8 @@ pub const VTable = struct { processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File, processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize, - lockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!LockedStderr, - tryLockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!?LockedStderr, + lockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!LockedStderr, + tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr, unlockStderr: *const fn (?*anyopaque) void, processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, @@ -2190,6 +2190,23 @@ pub const LockedStderr = struct { .mode = ls.terminal_mode, }; } + + pub fn clear(ls: LockedStderr, buffer: []u8) Cancelable!void { + const fw = ls.file_writer; + std.Progress.clearWrittenWithEscapeCodes(fw) catch |err| switch (err) { + error.WriteFailed => switch (fw.err.?) { + error.Canceled => |e| return e, + else => {}, + }, + }; + fw.interface.flush() catch |err| switch (err) { + error.WriteFailed => switch (fw.err.?) { + error.Canceled => |e| return e, + else => {}, + }, + }; + fw.interface.buffer = buffer; + } }; /// For doing application-level writes to the standard error stream. @@ -2200,12 +2217,16 @@ pub const LockedStderr = struct { /// See also: /// * `tryLockStderr` pub fn lockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancelable!LockedStderr { - return io.vtable.lockStderr(io.userdata, buffer, terminal_mode); + const ls = try io.vtable.lockStderr(io.userdata, terminal_mode); + try ls.clear(buffer); + return ls; } /// Same as `lockStderr` but non-blocking. pub fn tryLockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancelable!?LockedStderr { - return io.vtable.tryLockStderr(io.userdata, buffer, terminal_mode); + const ls = (try io.vtable.tryLockStderr(io.userdata, buffer, terminal_mode)) orelse return null; + try ls.clear(buffer); + return ls; } pub fn unlockStderr(io: Io) void { diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 3a1b5145610c9042fe8413a4283457be195cf7a5..444ca491a040ce028d7a4fb055863f89b951f0f3 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -10864,33 +10864,21 @@ fn netLookupFallible( return error.OptionUnsupported; } -fn lockStderr( - userdata: ?*anyopaque, - buffer: []u8, - terminal_mode: ?Io.Terminal.Mode, -) Io.Cancelable!Io.LockedStderr { +fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { const t: *Threaded = @ptrCast(@alignCast(userdata)); // Only global mutex since this is Threaded. std.process.stderr_thread_mutex.lock(); - return initLockedStderr(t, buffer, terminal_mode); + return initLockedStderr(t, terminal_mode); } -fn tryLockStderr( - userdata: ?*anyopaque, - buffer: []u8, - terminal_mode: ?Io.Terminal.Mode, -) Io.Cancelable!?Io.LockedStderr { +fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr { const t: *Threaded = @ptrCast(@alignCast(userdata)); // Only global mutex since this is Threaded. if (!std.process.stderr_thread_mutex.tryLock()) return null; - return try initLockedStderr(t, buffer, terminal_mode); + return try initLockedStderr(t, terminal_mode); } -fn initLockedStderr( - t: *Threaded, - buffer: []u8, - terminal_mode: ?Io.Terminal.Mode, -) Io.Cancelable!Io.LockedStderr { +fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { if (!t.stderr_writer_initialized) { const io_t = ioBasic(t); if (is_windows) t.stderr_writer.file = .stderr(); @@ -10901,19 +10889,6 @@ fn initLockedStderr( const CLICOLOR_FORCE = t.environ.exist.CLICOLOR_FORCE; t.stderr_mode = terminal_mode orelse try .detect(io_t, t.stderr_writer.file, NO_COLOR, CLICOLOR_FORCE); } - std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch |err| switch (err) { - error.WriteFailed => switch (t.stderr_writer.err.?) { - error.Canceled => |e| return e, - else => {}, - }, - }; - t.stderr_writer.interface.flush() catch |err| switch (err) { - error.WriteFailed => switch (t.stderr_writer.err.?) { - error.Canceled => |e| return e, - else => {}, - }, - }; - t.stderr_writer.interface.buffer = buffer; return .{ .file_writer = &t.stderr_writer, .terminal_mode = terminal_mode orelse t.stderr_mode, diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 6baa24d246f72f550f5f667109abb920a048a14c..f0baca2784f0ae912488d1ed4a0a1dad28bf1406 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -525,8 +525,8 @@ pub fn start(io: Io, options: Options) Node { if (switch (global_progress.terminal_mode) { .off => unreachable, // handled a few lines above - .ansi_escape_codes => io.concurrent(updateThreadRun, .{io}), - .windows_api => if (is_windows) io.concurrent(windowsApiUpdateThreadRun, .{io}) else unreachable, + .ansi_escape_codes => io.concurrent(updateTask, .{io}), + .windows_api => if (is_windows) io.concurrent(windowsApiUpdateTask, .{io}) else unreachable, }) |future| { global_progress.update_worker = future; } else |err| { @@ -561,18 +561,23 @@ fn wait(io: Io, timeout_ns: u64) bool { return resize_flag or (global_progress.cols == 0); } -fn updateThreadRun(io: Io) void { +fn updateTask(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; + // In this function we bypass the wrapper code inside `Io.lockStderr` / + // `Io.tryLockStderr` in order to avoid clearing the terminal twice. + // We still want to go through the `Io` instance however in case it uses a + // task-switching mutex. + { const resize_flag = wait(io, global_progress.initial_delay_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; maybeUpdateSize(resize_flag); const buffer, _ = computeRedraw(&serialized_buffer); - if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { + if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { defer io.unlockStderr(); global_progress.need_clear = true; locked_stderr.file_writer.interface.writeAll(buffer) catch return; @@ -583,7 +588,7 @@ fn updateThreadRun(io: Io) void { const resize_flag = wait(io, global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) { - const stderr = io.lockStderr(&.{}, null) catch return; + const stderr = io.vtable.lockStderr(io.userdata, null) catch return; defer io.unlockStderr(); return clearWrittenWithEscapeCodes(stderr.file_writer) catch {}; } @@ -591,7 +596,7 @@ fn updateThreadRun(io: Io) void { maybeUpdateSize(resize_flag); const buffer, _ = computeRedraw(&serialized_buffer); - if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { + if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { defer io.unlockStderr(); global_progress.need_clear = true; locked_stderr.file_writer.interface.writeAll(buffer) catch return; @@ -607,16 +612,21 @@ fn windowsApiWriteMarker() void { _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null); } -fn windowsApiUpdateThreadRun(io: Io) void { +fn windowsApiUpdateTask(io: Io) void { var serialized_buffer: Serialized.Buffer = undefined; + // In this function we bypass the wrapper code inside `Io.lockStderr` / + // `Io.tryLockStderr` in order to avoid clearing the terminal twice. + // We still want to go through the `Io` instance however in case it uses a + // task-switching mutex. + { const resize_flag = wait(io, global_progress.initial_delay_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; maybeUpdateSize(resize_flag); const buffer, const nl_n = computeRedraw(&serialized_buffer); - if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { + if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { defer io.unlockStderr(); windowsApiWriteMarker(); global_progress.need_clear = true; @@ -629,7 +639,7 @@ fn windowsApiUpdateThreadRun(io: Io) void { const resize_flag = wait(io, global_progress.refresh_rate_ns); if (@atomicLoad(bool, &global_progress.done, .monotonic)) { - _ = io.lockStderr(&.{}, null) catch return; + _ = io.vtable.lockStderr(io.userdata, null) catch return; defer io.unlockStderr(); return clearWrittenWindowsApi() catch {}; } @@ -637,7 +647,7 @@ fn windowsApiUpdateThreadRun(io: Io) void { maybeUpdateSize(resize_flag); const buffer, const nl_n = computeRedraw(&serialized_buffer); - if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { + if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { defer io.unlockStderr(); clearWrittenWindowsApi() catch return; windowsApiWriteMarker();