aboutsummaryrefslogtreecommitdiff
path: root/lib/std/Progress.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-03-16 17:33:24 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-03-16 17:33:24 -0700
commit1ed569e0b23c4432cd00604dcae89a17edc852a9 (patch)
tree090e0b3817a0caa4f3e7b99ec1d4d965f2bc7438 /lib/std/Progress.zig
parent778ca2ae6bf025edb6babeec08c957be1fbb37a5 (diff)
parentb4d58e93ea4d0bbfe674f80d301279d302fe8fc8 (diff)
downloadzig-1ed569e0b23c4432cd00604dcae89a17edc852a9.tar.gz
zig-1ed569e0b23c4432cd00604dcae89a17edc852a9.zip
Merge remote-tracking branch 'origin/master' into llvm16
Diffstat (limited to 'lib/std/Progress.zig')
-rw-r--r--lib/std/Progress.zig98
1 files changed, 72 insertions, 26 deletions
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
index f0b0e2dbd5..dba7166398 100644
--- a/lib/std/Progress.zig
+++ b/lib/std/Progress.zig
@@ -126,6 +126,21 @@ pub const Node = struct {
}
}
+ /// Thread-safe.
+ pub fn setName(self: *Node, name: []const u8) void {
+ const progress = self.context;
+ progress.update_mutex.lock();
+ defer progress.update_mutex.unlock();
+ self.name = name;
+ if (self.parent) |parent| {
+ @atomicStore(?*Node, &parent.recently_updated_child, self, .Release);
+ if (parent.parent) |grand_parent| {
+ @atomicStore(?*Node, &grand_parent.recently_updated_child, parent, .Release);
+ }
+ if (progress.timer) |*timer| progress.maybeRefreshWithHeldLock(timer);
+ }
+ }
+
/// Thread-safe. 0 means unknown.
pub fn setEstimatedTotalItems(self: *Node, count: usize) void {
@atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic);
@@ -174,16 +189,20 @@ pub fn maybeRefresh(self: *Progress) void {
if (self.timer) |*timer| {
if (!self.update_mutex.tryLock()) return;
defer self.update_mutex.unlock();
- const now = timer.read();
- if (now < self.initial_delay_ns) return;
- // TODO I have observed this to happen sometimes. I think we need to follow Rust's
- // lead and guarantee monotonically increasing times in the std lib itself.
- if (now < self.prev_refresh_timestamp) return;
- if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
- return self.refreshWithHeldLock();
+ maybeRefreshWithHeldLock(self, timer);
}
}
+fn maybeRefreshWithHeldLock(self: *Progress, timer: *std.time.Timer) void {
+ const now = timer.read();
+ if (now < self.initial_delay_ns) return;
+ // TODO I have observed this to happen sometimes. I think we need to follow Rust's
+ // lead and guarantee monotonically increasing times in the std lib itself.
+ if (now < self.prev_refresh_timestamp) return;
+ if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
+ return self.refreshWithHeldLock();
+}
+
/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.
pub fn refresh(self: *Progress) void {
if (!self.update_mutex.tryLock()) return;
@@ -192,32 +211,28 @@ pub fn refresh(self: *Progress) void {
return self.refreshWithHeldLock();
}
-fn refreshWithHeldLock(self: *Progress) void {
- const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
- if (is_dumb and self.dont_print_on_dumb) return;
-
- const file = self.terminal orelse return;
-
- var end: usize = 0;
- if (self.columns_written > 0) {
+fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void {
+ const file = p.terminal orelse return;
+ var end = end_ptr.*;
+ if (p.columns_written > 0) {
// restore the cursor position by moving the cursor
// `columns_written` cells to the left, then clear the rest of the
// line
- if (self.supports_ansi_escape_codes) {
- end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
- end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
+ if (p.supports_ansi_escape_codes) {
+ end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len;
+ end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
} else if (builtin.os.tag == .windows) winapi: {
- std.debug.assert(self.is_windows_terminal);
+ std.debug.assert(p.is_windows_terminal);
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
// stop trying to write to this file
- self.terminal = null;
+ p.terminal = null;
break :winapi;
}
var cursor_pos = windows.COORD{
- .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
+ .X = info.dwCursorPosition.X - @intCast(windows.SHORT, p.columns_written),
.Y = info.dwCursorPosition.Y,
};
@@ -235,7 +250,7 @@ fn refreshWithHeldLock(self: *Progress) void {
&written,
) != windows.TRUE) {
// stop trying to write to this file
- self.terminal = null;
+ p.terminal = null;
break :winapi;
}
if (windows.kernel32.FillConsoleOutputCharacterW(
@@ -246,22 +261,33 @@ fn refreshWithHeldLock(self: *Progress) void {
&written,
) != windows.TRUE) {
// stop trying to write to this file
- self.terminal = null;
+ p.terminal = null;
break :winapi;
}
if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) {
// stop trying to write to this file
- self.terminal = null;
+ p.terminal = null;
break :winapi;
}
} else {
// we are in a "dumb" terminal like in acme or writing to a file
- self.output_buffer[end] = '\n';
+ p.output_buffer[end] = '\n';
end += 1;
}
- self.columns_written = 0;
+ p.columns_written = 0;
}
+ end_ptr.* = end;
+}
+
+fn refreshWithHeldLock(self: *Progress) void {
+ const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
+ if (is_dumb and self.dont_print_on_dumb) return;
+
+ const file = self.terminal orelse return;
+
+ var end: usize = 0;
+ clearWithHeldLock(self, &end);
if (!self.done) {
var need_ellipse = false;
@@ -318,6 +344,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
self.columns_written = 0;
}
+/// Allows the caller to freely write to stderr until unlock_stderr() is called.
+/// During the lock, the progress information is cleared from the terminal.
+pub fn lock_stderr(p: *Progress) void {
+ p.update_mutex.lock();
+ if (p.terminal) |file| {
+ var end: usize = 0;
+ clearWithHeldLock(p, &end);
+ _ = file.write(p.output_buffer[0..end]) catch {
+ // stop trying to write to this file
+ p.terminal = null;
+ };
+ }
+ std.debug.getStderrMutex().lock();
+}
+
+pub fn unlock_stderr(p: *Progress) void {
+ std.debug.getStderrMutex().unlock();
+ p.update_mutex.unlock();
+}
+
fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
const amt = written.len;