From 3a768bd6ce9e891ad258b32fb2dd0ed912ef9e9b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 23 May 2024 18:45:04 -0700 Subject: [PATCH] std.Progress: save a copy of IPC data so that the previous message can be used when the pipe is empty. prevents flickering --- lib/std/Progress.zig | 90 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 76ef51943d..4efb8bb194 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -472,6 +472,7 @@ const Serialized = struct { fn serialize() Serialized { var serialized_len: usize = 0; + var any_ipc = false; // Iterate all of the nodes and construct a serializable copy of the state that can be examined // without atomics. @@ -486,6 +487,8 @@ fn serialize() Serialized { dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic); dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .monotonic); + any_ipc = any_ipc or dest_storage.getIpcFd() != null; + const end_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst); if (begin_parent == end_parent) { serialized_node_parents_buffer[serialized_len] = begin_parent; @@ -508,6 +511,32 @@ fn serialize() Serialized { } // Find nodes which correspond to child processes. + if (any_ipc) + serialized_len = serializeIpc(serialized_len); + + return .{ + .parents = serialized_node_parents_buffer[0..serialized_len], + .storage = serialized_node_storage_buffer[0..serialized_len], + }; +} + +var parents_copy: [default_node_storage_buffer_len]Node.Parent = undefined; +var storage_copy: [default_node_storage_buffer_len]Node.Storage = undefined; + +const SavedMetadata = extern struct { + start_index: u16, + nodes_len: u16, + main_index: u16, + flags: Flags, + + const Flags = enum(u16) { + saved = std.math.maxInt(u16), + _, + }; +}; + +fn serializeIpc(start_serialized_len: usize) usize { + var serialized_len = start_serialized_len; var pipe_buf: [4096]u8 align(4) = undefined; for ( @@ -532,24 +561,23 @@ fn serialize() Serialized { // Ignore all but the last message on the pipe. var input: []align(2) u8 = pipe_buf[0..bytes_read]; if (input.len == 0) { - main_storage.completed_count = 0; - main_storage.estimated_total_count = 0; + serialized_len = useSavedIpcData(serialized_len, main_storage, main_index); continue; } const storage, const parents = while (true) { if (input.len < 4) { std.log.warn("short read: {d} out of 4 header bytes", .{input.len}); - main_storage.completed_count = 0; - main_storage.estimated_total_count = 0; + // TODO keep track of the short read to trash odd bytes with the next read + serialized_len = useSavedIpcData(serialized_len, main_storage, main_index); continue; } const subtree_len = std.mem.readInt(u32, input[0..4], .little); const expected_bytes = 4 + subtree_len * (@sizeOf(Node.Storage) + @sizeOf(Node.Parent)); if (input.len < expected_bytes) { std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len }); - main_storage.completed_count = 0; - main_storage.estimated_total_count = 0; + // TODO keep track of the short read to trash odd bytes with the next read + serialized_len = useSavedIpcData(serialized_len, main_storage, main_index); continue; } if (input.len > expected_bytes) { @@ -564,6 +592,14 @@ fn serialize() Serialized { }; }; + // Remember in case the pipe is empty on next update. + @as(*SavedMetadata, @ptrCast(&main_storage.name)).* = .{ + .start_index = @intCast(serialized_len), + .nodes_len = @intCast(parents.len), + .main_index = @intCast(main_index), + .flags = .saved, + }; + // Mount the root here. main_storage.* = storage[0]; @@ -580,6 +616,7 @@ fn serialize() Serialized { // Root node is being mounted here. @as(Node.Parent, @enumFromInt(0)) => @enumFromInt(main_index), // Other nodes mounted at the end. + // TODO check for bad data pointing outside the expected range _ => |off| @enumFromInt(serialized_len + @intFromEnum(off) - 1), }; } @@ -587,10 +624,43 @@ fn serialize() Serialized { serialized_len += storage.len - 1; } - return .{ - .parents = serialized_node_parents_buffer[0..serialized_len], - .storage = serialized_node_storage_buffer[0..serialized_len], - }; + // Save a copy in case any pipes are empty on the next update. + @memcpy(parents_copy[0..serialized_len], serialized_node_parents_buffer[0..serialized_len]); + @memcpy(storage_copy[0..serialized_len], serialized_node_storage_buffer[0..serialized_len]); + + return serialized_len; +} + +fn useSavedIpcData(start_serialized_len: usize, main_storage: *Node.Storage, main_index: usize) usize { + const saved_metadata: *SavedMetadata = @ptrCast(&main_storage.name); + if (saved_metadata.flags != .saved) { + main_storage.completed_count = 0; + main_storage.estimated_total_count = 0; + return start_serialized_len; + } + + const start_index = saved_metadata.start_index; + const nodes_len = saved_metadata.nodes_len; + const old_main_index = saved_metadata.main_index; + + const parents = parents_copy[start_index..][0 .. nodes_len - 1]; + const storage = storage_copy[start_index..][0 .. nodes_len - 1]; + + main_storage.* = storage_copy[old_main_index]; + + @memcpy(serialized_node_storage_buffer[start_serialized_len..][0..storage.len], storage); + + for (serialized_node_parents_buffer[start_serialized_len..][0..parents.len], parents) |*dest, p| { + dest.* = switch (p) { + .none, .unused => .none, + _ => |prev| @enumFromInt(if (@intFromEnum(prev) == old_main_index) + main_index + else + @intFromEnum(prev) - start_index + start_serialized_len), + }; + } + + return start_serialized_len + storage.len; } fn computeRedraw() []u8 {