| author | |
| committer | |
| log | f862762f091637ad0d17c4735226c0c477c2cb3e |
| tree | 48264a8d8bf0e5e461182edd84f7b08239845858 |
| parent | ec13e8eaaafc34193f245e593333f512681f168f |
move some of the clearing logic from std.Io.Threaded to the Io interface
layer, thus allowing it to be skipped by advanced usage code via calling
the vtable functions directly.
then take advantage of this in std.Progress to avoid clearing the
terminal twice.
closes #306113 files changed, 50 insertions(+), 44 deletions(-)
lib/std/Io.zig+25-4| ... | ... | @@ -713,8 +713,8 @@ pub const VTable = struct { |
| 713 | 713 | |
| 714 | 714 | processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File, |
| 715 | 715 | processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize, |
| 716 | lockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!LockedStderr, | |
| 717 | tryLockStderr: *const fn (?*anyopaque, buffer: []u8, ?Terminal.Mode) Cancelable!?LockedStderr, | |
| 716 | lockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!LockedStderr, | |
| 717 | tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr, | |
| 718 | 718 | unlockStderr: *const fn (?*anyopaque) void, |
| 719 | 719 | processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void, |
| 720 | 720 | |
| ... | ... | @@ -2190,6 +2190,23 @@ pub const LockedStderr = struct { |
| 2190 | 2190 | .mode = ls.terminal_mode, |
| 2191 | 2191 | }; |
| 2192 | 2192 | } |
| 2193 | ||
| 2194 | pub fn clear(ls: LockedStderr, buffer: []u8) Cancelable!void { | |
| 2195 | const fw = ls.file_writer; | |
| 2196 | std.Progress.clearWrittenWithEscapeCodes(fw) catch |err| switch (err) { | |
| 2197 | error.WriteFailed => switch (fw.err.?) { | |
| 2198 | error.Canceled => |e| return e, | |
| 2199 | else => {}, | |
| 2200 | }, | |
| 2201 | }; | |
| 2202 | fw.interface.flush() catch |err| switch (err) { | |
| 2203 | error.WriteFailed => switch (fw.err.?) { | |
| 2204 | error.Canceled => |e| return e, | |
| 2205 | else => {}, | |
| 2206 | }, | |
| 2207 | }; | |
| 2208 | fw.interface.buffer = buffer; | |
| 2209 | } | |
| 2193 | 2210 | }; |
| 2194 | 2211 | |
| 2195 | 2212 | /// For doing application-level writes to the standard error stream. |
| ... | ... | @@ -2200,12 +2217,16 @@ pub const LockedStderr = struct { |
| 2200 | 2217 | /// See also: |
| 2201 | 2218 | /// * `tryLockStderr` |
| 2202 | 2219 | pub fn lockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancelable!LockedStderr { |
| 2203 | return io.vtable.lockStderr(io.userdata, buffer, terminal_mode); | |
| 2220 | const ls = try io.vtable.lockStderr(io.userdata, terminal_mode); | |
| 2221 | try ls.clear(buffer); | |
| 2222 | return ls; | |
| 2204 | 2223 | } |
| 2205 | 2224 | |
| 2206 | 2225 | /// Same as `lockStderr` but non-blocking. |
| 2207 | 2226 | pub fn tryLockStderr(io: Io, buffer: []u8, terminal_mode: ?Terminal.Mode) Cancelable!?LockedStderr { |
| 2208 | return io.vtable.tryLockStderr(io.userdata, buffer, terminal_mode); | |
| 2227 | const ls = (try io.vtable.tryLockStderr(io.userdata, buffer, terminal_mode)) orelse return null; | |
| 2228 | try ls.clear(buffer); | |
| 2229 | return ls; | |
| 2209 | 2230 | } |
| 2210 | 2231 | |
| 2211 | 2232 | pub fn unlockStderr(io: Io) void { |
lib/std/Io/Threaded.zig+5-30| ... | ... | @@ -10864,33 +10864,21 @@ fn netLookupFallible( |
| 10864 | 10864 | return error.OptionUnsupported; |
| 10865 | 10865 | } |
| 10866 | 10866 | |
| 10867 | fn lockStderr( | |
| 10868 | userdata: ?*anyopaque, | |
| 10869 | buffer: []u8, | |
| 10870 | terminal_mode: ?Io.Terminal.Mode, | |
| 10871 | ) Io.Cancelable!Io.LockedStderr { | |
| 10867 | fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { | |
| 10872 | 10868 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 10873 | 10869 | // Only global mutex since this is Threaded. |
| 10874 | 10870 | std.process.stderr_thread_mutex.lock(); |
| 10875 | return initLockedStderr(t, buffer, terminal_mode); | |
| 10871 | return initLockedStderr(t, terminal_mode); | |
| 10876 | 10872 | } |
| 10877 | 10873 | |
| 10878 | fn tryLockStderr( | |
| 10879 | userdata: ?*anyopaque, | |
| 10880 | buffer: []u8, | |
| 10881 | terminal_mode: ?Io.Terminal.Mode, | |
| 10882 | ) Io.Cancelable!?Io.LockedStderr { | |
| 10874 | fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr { | |
| 10883 | 10875 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 10884 | 10876 | // Only global mutex since this is Threaded. |
| 10885 | 10877 | if (!std.process.stderr_thread_mutex.tryLock()) return null; |
| 10886 | return try initLockedStderr(t, buffer, terminal_mode); | |
| 10878 | return try initLockedStderr(t, terminal_mode); | |
| 10887 | 10879 | } |
| 10888 | 10880 | |
| 10889 | fn initLockedStderr( | |
| 10890 | t: *Threaded, | |
| 10891 | buffer: []u8, | |
| 10892 | terminal_mode: ?Io.Terminal.Mode, | |
| 10893 | ) Io.Cancelable!Io.LockedStderr { | |
| 10881 | fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr { | |
| 10894 | 10882 | if (!t.stderr_writer_initialized) { |
| 10895 | 10883 | const io_t = ioBasic(t); |
| 10896 | 10884 | if (is_windows) t.stderr_writer.file = .stderr(); |
| ... | ... | @@ -10901,19 +10889,6 @@ fn initLockedStderr( |
| 10901 | 10889 | const CLICOLOR_FORCE = t.environ.exist.CLICOLOR_FORCE; |
| 10902 | 10890 | t.stderr_mode = terminal_mode orelse try .detect(io_t, t.stderr_writer.file, NO_COLOR, CLICOLOR_FORCE); |
| 10903 | 10891 | } |
| 10904 | std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch |err| switch (err) { | |
| 10905 | error.WriteFailed => switch (t.stderr_writer.err.?) { | |
| 10906 | error.Canceled => |e| return e, | |
| 10907 | else => {}, | |
| 10908 | }, | |
| 10909 | }; | |
| 10910 | t.stderr_writer.interface.flush() catch |err| switch (err) { | |
| 10911 | error.WriteFailed => switch (t.stderr_writer.err.?) { | |
| 10912 | error.Canceled => |e| return e, | |
| 10913 | else => {}, | |
| 10914 | }, | |
| 10915 | }; | |
| 10916 | t.stderr_writer.interface.buffer = buffer; | |
| 10917 | 10892 | return .{ |
| 10918 | 10893 | .file_writer = &t.stderr_writer, |
| 10919 | 10894 | .terminal_mode = terminal_mode orelse t.stderr_mode, |
lib/std/Progress.zig+20-10| ... | ... | @@ -525,8 +525,8 @@ pub fn start(io: Io, options: Options) Node { |
| 525 | 525 | |
| 526 | 526 | if (switch (global_progress.terminal_mode) { |
| 527 | 527 | .off => unreachable, // handled a few lines above |
| 528 | .ansi_escape_codes => io.concurrent(updateThreadRun, .{io}), | |
| 529 | .windows_api => if (is_windows) io.concurrent(windowsApiUpdateThreadRun, .{io}) else unreachable, | |
| 528 | .ansi_escape_codes => io.concurrent(updateTask, .{io}), | |
| 529 | .windows_api => if (is_windows) io.concurrent(windowsApiUpdateTask, .{io}) else unreachable, | |
| 530 | 530 | }) |future| { |
| 531 | 531 | global_progress.update_worker = future; |
| 532 | 532 | } else |err| { |
| ... | ... | @@ -561,18 +561,23 @@ fn wait(io: Io, timeout_ns: u64) bool { |
| 561 | 561 | return resize_flag or (global_progress.cols == 0); |
| 562 | 562 | } |
| 563 | 563 | |
| 564 | fn updateThreadRun(io: Io) void { | |
| 564 | fn updateTask(io: Io) void { | |
| 565 | 565 | // Store this data in the thread so that it does not need to be part of the |
| 566 | 566 | // linker data of the main executable. |
| 567 | 567 | var serialized_buffer: Serialized.Buffer = undefined; |
| 568 | 568 | |
| 569 | // In this function we bypass the wrapper code inside `Io.lockStderr` / | |
| 570 | // `Io.tryLockStderr` in order to avoid clearing the terminal twice. | |
| 571 | // We still want to go through the `Io` instance however in case it uses a | |
| 572 | // task-switching mutex. | |
| 573 | ||
| 569 | 574 | { |
| 570 | 575 | const resize_flag = wait(io, global_progress.initial_delay_ns); |
| 571 | 576 | if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; |
| 572 | 577 | maybeUpdateSize(resize_flag); |
| 573 | 578 | |
| 574 | 579 | const buffer, _ = computeRedraw(&serialized_buffer); |
| 575 | if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { | |
| 580 | if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { | |
| 576 | 581 | defer io.unlockStderr(); |
| 577 | 582 | global_progress.need_clear = true; |
| 578 | 583 | locked_stderr.file_writer.interface.writeAll(buffer) catch return; |
| ... | ... | @@ -583,7 +588,7 @@ fn updateThreadRun(io: Io) void { |
| 583 | 588 | const resize_flag = wait(io, global_progress.refresh_rate_ns); |
| 584 | 589 | |
| 585 | 590 | if (@atomicLoad(bool, &global_progress.done, .monotonic)) { |
| 586 | const stderr = io.lockStderr(&.{}, null) catch return; | |
| 591 | const stderr = io.vtable.lockStderr(io.userdata, null) catch return; | |
| 587 | 592 | defer io.unlockStderr(); |
| 588 | 593 | return clearWrittenWithEscapeCodes(stderr.file_writer) catch {}; |
| 589 | 594 | } |
| ... | ... | @@ -591,7 +596,7 @@ fn updateThreadRun(io: Io) void { |
| 591 | 596 | maybeUpdateSize(resize_flag); |
| 592 | 597 | |
| 593 | 598 | const buffer, _ = computeRedraw(&serialized_buffer); |
| 594 | if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { | |
| 599 | if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { | |
| 595 | 600 | defer io.unlockStderr(); |
| 596 | 601 | global_progress.need_clear = true; |
| 597 | 602 | locked_stderr.file_writer.interface.writeAll(buffer) catch return; |
| ... | ... | @@ -607,16 +612,21 @@ fn windowsApiWriteMarker() void { |
| 607 | 612 | _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null); |
| 608 | 613 | } |
| 609 | 614 | |
| 610 | fn windowsApiUpdateThreadRun(io: Io) void { | |
| 615 | fn windowsApiUpdateTask(io: Io) void { | |
| 611 | 616 | var serialized_buffer: Serialized.Buffer = undefined; |
| 612 | 617 | |
| 618 | // In this function we bypass the wrapper code inside `Io.lockStderr` / | |
| 619 | // `Io.tryLockStderr` in order to avoid clearing the terminal twice. | |
| 620 | // We still want to go through the `Io` instance however in case it uses a | |
| 621 | // task-switching mutex. | |
| 622 | ||
| 613 | 623 | { |
| 614 | 624 | const resize_flag = wait(io, global_progress.initial_delay_ns); |
| 615 | 625 | if (@atomicLoad(bool, &global_progress.done, .monotonic)) return; |
| 616 | 626 | maybeUpdateSize(resize_flag); |
| 617 | 627 | |
| 618 | 628 | const buffer, const nl_n = computeRedraw(&serialized_buffer); |
| 619 | if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { | |
| 629 | if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { | |
| 620 | 630 | defer io.unlockStderr(); |
| 621 | 631 | windowsApiWriteMarker(); |
| 622 | 632 | global_progress.need_clear = true; |
| ... | ... | @@ -629,7 +639,7 @@ fn windowsApiUpdateThreadRun(io: Io) void { |
| 629 | 639 | const resize_flag = wait(io, global_progress.refresh_rate_ns); |
| 630 | 640 | |
| 631 | 641 | if (@atomicLoad(bool, &global_progress.done, .monotonic)) { |
| 632 | _ = io.lockStderr(&.{}, null) catch return; | |
| 642 | _ = io.vtable.lockStderr(io.userdata, null) catch return; | |
| 633 | 643 | defer io.unlockStderr(); |
| 634 | 644 | return clearWrittenWindowsApi() catch {}; |
| 635 | 645 | } |
| ... | ... | @@ -637,7 +647,7 @@ fn windowsApiUpdateThreadRun(io: Io) void { |
| 637 | 647 | maybeUpdateSize(resize_flag); |
| 638 | 648 | |
| 639 | 649 | const buffer, const nl_n = computeRedraw(&serialized_buffer); |
| 640 | if (io.tryLockStderr(&.{}, null) catch return) |locked_stderr| { | |
| 650 | if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| { | |
| 641 | 651 | defer io.unlockStderr(); |
| 642 | 652 | clearWrittenWindowsApi() catch return; |
| 643 | 653 | windowsApiWriteMarker(); |