commit dc3a192ae841706bb965218c68b0085bbec2b35e (tree)
parent 6145819c0ba00924b37bab78200aeab6306c1672
Author: Andrew Kelley <andrew@ziglang.org>
Date: Mon, 27 May 2024 10:29:39 -0700
std.Progress: count newlines more accurately
Split newline_count into written_newline_count and
accumulated_newline_count. This handle the case when the tryLock() fails
to obtain the lock, because in such case there would not be any newlines
written to the terminal but the system would incorrectly think there
were. Now, written_newline_count is only adjusted when the write() call
succeeds.
Furthermore, write() call failure is handled by exiting the update
thread.
Diffstat:
1 file changed, 21 insertions(+), 16 deletions(-)
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
@@ -32,9 +32,11 @@ initial_delay_ns: u64,
rows: u16,
cols: u16,
-/// Needed because terminal escape codes require one to take scrolling into
-/// account.
-newline_count: u16,
+/// Tracks the number of newlines that have been actually written to the terminal.
+written_newline_count: u16,
+/// Tracks the number of newlines that will be written to the terminal if the
+/// draw buffer is sent.
+accumulated_newline_count: u16,
/// Accessed only by the update thread.
draw_buffer: []u8,
@@ -284,7 +286,8 @@ var global_progress: Progress = .{
.initial_delay_ns = undefined,
.rows = 0,
.cols = 0,
- .newline_count = 0,
+ .written_newline_count = 0,
+ .accumulated_newline_count = 0,
.draw_buffer = undefined,
.done = false,
@@ -423,7 +426,7 @@ fn updateThreadRun() void {
const buffer = computeRedraw(&serialized_buffer);
if (stderr_mutex.tryLock()) {
defer stderr_mutex.unlock();
- write(buffer);
+ write(buffer) catch return;
}
}
@@ -440,7 +443,7 @@ fn updateThreadRun() void {
const buffer = computeRedraw(&serialized_buffer);
if (stderr_mutex.tryLock()) {
defer stderr_mutex.unlock();
- write(buffer);
+ write(buffer) catch return;
}
}
}
@@ -499,7 +502,7 @@ const tree_line = "\x1B\x28\x30\x78\x1B\x28\x42 "; // │
const tree_langle = "\x1B\x28\x30\x6d\x71\x1B\x28\x42 "; // └─
fn clearTerminal() void {
- if (global_progress.newline_count == 0) return;
+ if (global_progress.written_newline_count == 0) return;
var i: usize = 0;
const buf = global_progress.draw_buffer;
@@ -512,15 +515,17 @@ fn clearTerminal() void {
buf[i..][0..finish_sync.len].* = finish_sync.*;
i += finish_sync.len;
- write(buf[0..i]);
+ global_progress.accumulated_newline_count = 0;
+ write(buf[0..i]) catch {
+ global_progress.terminal = null;
+ };
}
fn computeClear(buf: []u8, start_i: usize) usize {
var i = start_i;
- const prev_nl_n = global_progress.newline_count;
+ const prev_nl_n = global_progress.written_newline_count;
if (prev_nl_n > 0) {
- global_progress.newline_count = 0;
buf[i] = '\r';
i += 1;
for (0..prev_nl_n) |_| {
@@ -854,6 +859,7 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 {
i = computeClear(buf, i);
+ global_progress.accumulated_newline_count = 0;
const root_node_index: Node.Index = @enumFromInt(0);
i = computeNode(buf, i, serialized, children, root_node_index);
@@ -937,7 +943,7 @@ fn computeNode(
i = @min(global_progress.cols + start_i, i);
buf[i] = '\n';
i += 1;
- global_progress.newline_count += 1;
+ global_progress.accumulated_newline_count += 1;
}
if (global_progress.withinRowLimit()) {
@@ -959,14 +965,13 @@ fn withinRowLimit(p: *Progress) bool {
// The +2 here is so that the PS1 is not scrolled off the top of the terminal.
// one because we keep the cursor on the next line
// one more to account for the PS1
- return p.newline_count + 2 < p.rows;
+ return p.accumulated_newline_count + 2 < p.rows;
}
-fn write(buf: []const u8) void {
+fn write(buf: []const u8) anyerror!void {
const tty = global_progress.terminal orelse return;
- tty.writeAll(buf) catch {
- global_progress.terminal = null;
- };
+ try tty.writeAll(buf);
+ global_progress.written_newline_count = global_progress.accumulated_newline_count;
}
fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {