From 5a00e249632716b86edac088f69d19d82e307a28 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 17:26:55 -0700 Subject: [PATCH] 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. --- lib/std/Progress.zig | 32 ++++++++++++++++++-------------- lib/std/special/test_runner.zig | 5 +---- src/Compilation.zig | 2 +- src/stage1.zig | 5 +---- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 682171366b..24b66c1162 100644 --- a/lib/std/Progress.zig +++ b/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{ diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index 9848cb5a3e..fb00a9dc30 100644 --- a/lib/std/special/test_runner.zig +++ b/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; diff --git a/src/Compilation.zig b/src/Compilation.zig index f07a7c9dd7..bd7581863b 100644 --- a/src/Compilation.zig +++ b/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; diff --git a/src/stage1.zig b/src/stage1.zig index b716a9c954..005dc312ba 100644 --- a/src/stage1.zig +++ b/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