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.
This commit is contained in:
Andrew Kelley 2024-05-27 10:29:39 -07:00
parent 6145819c0b
commit dc3a192ae8

View File

@ -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 {