authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-08 17:26:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-08 17:26:55-07:00
log5a00e249632716b86edac088f69d19d82e307a28
tree2daa825ca0033bd0fb4735c43134c3f9ef783f6c
parentfd6c351263e99af4dca73af953711e6d1b57f4e4

std.Progress: make the API infallible

by handling `error.TimerUnsupported`. In this case, only explicit calls to refresh() will cause the progress line to be printed.

4 files changed, 21 insertions(+), 23 deletions(-)

lib/std/Progress.zig+18-14
...@@ -35,7 +35,7 @@ root: Node = undefined,...@@ -35,7 +35,7 @@ root: Node = undefined,
3535
36/// Keeps track of how much time has passed since the beginning.36/// Keeps track of how much time has passed since the beginning.
37/// Used to compare with `initial_delay_ms` and `refresh_rate_ms`.37/// Used to compare with `initial_delay_ms` and `refresh_rate_ms`.
38timer: std.time.Timer = undefined,38timer: ?std.time.Timer = null,
3939
40/// When the previous refresh was written to the terminal.40/// When the previous refresh was written to the terminal.
41/// Used to compare with `refresh_rate_ms`.41/// Used to compare with `refresh_rate_ms`.
...@@ -139,7 +139,7 @@ pub const Node = struct {...@@ -139,7 +139,7 @@ pub const Node = struct {
139/// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this139/// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
140/// API to return Progress rather than accept it as a parameter.140/// API to return Progress rather than accept it as a parameter.
141/// `estimated_total_items` value of 0 means unknown.141/// `estimated_total_items` value of 0 means unknown.
142pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*Node {142pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) *Node {
143 const stderr = std.io.getStdErr();143 const stderr = std.io.getStdErr();
144 self.terminal = null;144 self.terminal = null;
145 if (stderr.supportsAnsiEscapeCodes()) {145 if (stderr.supportsAnsiEscapeCodes()) {
...@@ -161,22 +161,24 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*...@@ -161,22 +161,24 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
161 };161 };
162 self.columns_written = 0;162 self.columns_written = 0;
163 self.prev_refresh_timestamp = 0;163 self.prev_refresh_timestamp = 0;
164 self.timer = try std.time.Timer.start();164 self.timer = std.time.Timer.start() catch null;
165 self.done = false;165 self.done = false;
166 return &self.root;166 return &self.root;
167}167}
168168
169/// Updates the terminal if enough time has passed since last update. Thread-safe.169/// Updates the terminal if enough time has passed since last update. Thread-safe.
170pub fn maybeRefresh(self: *Progress) void {170pub fn maybeRefresh(self: *Progress) void {
171 const now = self.timer.read();171 if (self.timer) |*timer| {
172 if (now < self.initial_delay_ns) return;172 const now = timer.read();
173 if (!self.update_mutex.tryLock()) return;173 if (now < self.initial_delay_ns) return;
174 defer self.update_mutex.unlock();174 if (!self.update_mutex.tryLock()) return;
175 // TODO I have observed this to happen sometimes. I think we need to follow Rust's175 defer self.update_mutex.unlock();
176 // lead and guarantee monotonically increasing times in the std lib itself.176 // TODO I have observed this to happen sometimes. I think we need to follow Rust's
177 if (now < self.prev_refresh_timestamp) return;177 // lead and guarantee monotonically increasing times in the std lib itself.
178 if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;178 if (now < self.prev_refresh_timestamp) return;
179 return self.refreshWithHeldLock();179 if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
180 return self.refreshWithHeldLock();
181 }
180}182}
181183
182/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.184/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.
...@@ -285,7 +287,9 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -285,7 +287,9 @@ fn refreshWithHeldLock(self: *Progress) void {
285 // Stop trying to write to this file once it errors.287 // Stop trying to write to this file once it errors.
286 self.terminal = null;288 self.terminal = null;
287 };289 };
288 self.prev_refresh_timestamp = self.timer.read();290 if (self.timer) |*timer| {
291 self.prev_refresh_timestamp = timer.read();
292 }
289}293}
290294
291pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {295pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
...@@ -327,7 +331,7 @@ test "basic functionality" {...@@ -327,7 +331,7 @@ test "basic functionality" {
327 return error.SkipZigTest;331 return error.SkipZigTest;
328 }332 }
329 var progress = Progress{};333 var progress = Progress{};
330 const root_node = try progress.start("", 100);334 const root_node = progress.start("", 100);
331 defer root_node.end();335 defer root_node.end();
332336
333 const sub_task_names = [_][]const u8{337 const sub_task_names = [_][]const u8{
lib/std/special/test_runner.zig+1-4
...@@ -34,10 +34,7 @@ pub fn main() void {...@@ -34,10 +34,7 @@ pub fn main() void {
34 var progress = std.Progress{34 var progress = std.Progress{
35 .dont_print_on_dumb = true,35 .dont_print_on_dumb = true,
36 };36 };
37 const root_node = progress.start("Test", test_fn_list.len) catch |err| switch (err) {37 const root_node = progress.start("Test", test_fn_list.len);
38 // TODO still run tests in this case
39 error.TimerUnsupported => @panic("timer unsupported"),
40 };
41 const have_tty = progress.terminal != null and progress.supports_ansi_escape_codes;38 const have_tty = progress.terminal != null and progress.supports_ansi_escape_codes;
4239
43 var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined;40 var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined;
src/Compilation.zig+1-1
...@@ -2586,7 +2586,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2586,7 +2586,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2586 // If the terminal is dumb, we dont want to show the user all the2586 // If the terminal is dumb, we dont want to show the user all the
2587 // output.2587 // output.
2588 var progress: std.Progress = .{ .dont_print_on_dumb = true };2588 var progress: std.Progress = .{ .dont_print_on_dumb = true };
2589 var main_progress_node = try progress.start("", 0);2589 var main_progress_node = progress.start("", 0);
2590 defer main_progress_node.end();2590 defer main_progress_node.end();
2591 if (self.color == .off) progress.terminal = null;2591 if (self.color == .off) progress.terminal = null;
25922592
src/stage1.zig+1-4
...@@ -305,10 +305,7 @@ export fn stage2_progress_start_root(...@@ -305,10 +305,7 @@ export fn stage2_progress_start_root(
305 name_len: usize,305 name_len: usize,
306 estimated_total_items: usize,306 estimated_total_items: usize,
307) *std.Progress.Node {307) *std.Progress.Node {
308 return progress.start(308 return progress.start(name_ptr[0..name_len], estimated_total_items);
309 name_ptr[0..name_len],
310 estimated_total_items,
311 ) catch @panic("timer unsupported");
312}309}
313310
314// ABI warning311// ABI warning