Commit 5a00e24963

Andrew Kelley <andrew@ziglang.org>
2022-02-09 01:26:55
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.
1 parent fd6c351
Changed files (4)
lib/std/special/test_runner.zig
@@ -34,10 +34,7 @@ pub fn main() void {
     var progress = std.Progress{
         .dont_print_on_dumb = true,
     };
-    const root_node = progress.start("Test", test_fn_list.len) catch |err| switch (err) {
-        // TODO still run tests in this case
-        error.TimerUnsupported => @panic("timer unsupported"),
-    };
+    const root_node = progress.start("Test", test_fn_list.len);
     const have_tty = progress.terminal != null and progress.supports_ansi_escape_codes;
 
     var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined;
lib/std/Progress.zig
@@ -35,7 +35,7 @@ root: Node = undefined,
 
 /// Keeps track of how much time has passed since the beginning.
 /// Used to compare with `initial_delay_ms` and `refresh_rate_ms`.
-timer: std.time.Timer = undefined,
+timer: ?std.time.Timer = null,
 
 /// When the previous refresh was written to the terminal.
 /// Used to compare with `refresh_rate_ms`.
@@ -139,7 +139,7 @@ pub const Node = struct {
 /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
 /// API to return Progress rather than accept it as a parameter.
 /// `estimated_total_items` value of 0 means unknown.
-pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*Node {
+pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) *Node {
     const stderr = std.io.getStdErr();
     self.terminal = null;
     if (stderr.supportsAnsiEscapeCodes()) {
@@ -161,22 +161,24 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
     };
     self.columns_written = 0;
     self.prev_refresh_timestamp = 0;
-    self.timer = try std.time.Timer.start();
+    self.timer = std.time.Timer.start() catch null;
     self.done = false;
     return &self.root;
 }
 
 /// Updates the terminal if enough time has passed since last update. Thread-safe.
 pub fn maybeRefresh(self: *Progress) void {
-    const now = self.timer.read();
-    if (now < self.initial_delay_ns) return;
-    if (!self.update_mutex.tryLock()) return;
-    defer self.update_mutex.unlock();
-    // 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();
+    if (self.timer) |*timer| {
+        const now = timer.read();
+        if (now < self.initial_delay_ns) return;
+        if (!self.update_mutex.tryLock()) return;
+        defer self.update_mutex.unlock();
+        // 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.
@@ -285,7 +287,9 @@ fn refreshWithHeldLock(self: *Progress) void {
         // Stop trying to write to this file once it errors.
         self.terminal = null;
     };
-    self.prev_refresh_timestamp = self.timer.read();
+    if (self.timer) |*timer| {
+        self.prev_refresh_timestamp = timer.read();
+    }
 }
 
 pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
@@ -327,7 +331,7 @@ test "basic functionality" {
         return error.SkipZigTest;
     }
     var progress = Progress{};
-    const root_node = try progress.start("", 100);
+    const root_node = progress.start("", 100);
     defer root_node.end();
 
     const sub_task_names = [_][]const u8{
src/Compilation.zig
@@ -2586,7 +2586,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
     // If the terminal is dumb, we dont want to show the user all the
     // output.
     var progress: std.Progress = .{ .dont_print_on_dumb = true };
-    var main_progress_node = try progress.start("", 0);
+    var main_progress_node = progress.start("", 0);
     defer main_progress_node.end();
     if (self.color == .off) progress.terminal = null;
 
src/stage1.zig
@@ -305,10 +305,7 @@ export fn stage2_progress_start_root(
     name_len: usize,
     estimated_total_items: usize,
 ) *std.Progress.Node {
-    return progress.start(
-        name_ptr[0..name_len],
-        estimated_total_items,
-    ) catch @panic("timer unsupported");
+    return progress.start(name_ptr[0..name_len], estimated_total_items);
 }
 
 // ABI warning