authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-27 09:06:41-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-27 20:56:48-07:00
logea7d8ec14752575a68733742be2ee7a583eea49e
tree0d46205cd1eecde8fa29f1729fe2ebf6d45f7392
parent11f894702b7c06b87f6e94eff719d9f83eaeeddf

std.Progress: smaller type for parents and robustify

Switch Node.Parent, Node.Index, and Node.OptionalIndex to be backed by u8 rather than u16. This works fine since we use 200 as the preallocated node buffer. This has the nice property that scanning the entire parents array for allocated nodes fits in 4 cache lines, even if we bumped the 200 up to 254 (leaving room for the two special states). The thread that reads progress updates from the pipe now handles short reads by ignoring messages that are sent in multiple reads. When checking the terminal size, if there is a failure, fall back to a conservative guess of 80x25 rather than panicking. A debug message is also emitted which would be displayed only in a debug build.

1 files changed, 38 insertions(+), 26 deletions(-)

lib/std/Progress.zig+38-26
......@@ -104,11 +104,11 @@ pub const Node = struct {
104104 }
105105 };
106106
107 const Parent = enum(u16) {
107 const Parent = enum(u8) {
108108 /// Unallocated storage.
109 unused = std.math.maxInt(u16) - 1,
109 unused = std.math.maxInt(u8) - 1,
110110 /// Indicates root node.
111 none = std.math.maxInt(u16),
111 none = std.math.maxInt(u8),
112112 /// Index into `node_storage`.
113113 _,
114114
......@@ -120,8 +120,8 @@ pub const Node = struct {
120120 }
121121 };
122122
123 const OptionalIndex = enum(u16) {
124 none = std.math.maxInt(u16),
123 const OptionalIndex = enum(u8) {
124 none = std.math.maxInt(u8),
125125 /// Index into `node_storage`.
126126 _,
127127
......@@ -137,7 +137,7 @@ pub const Node = struct {
137137 };
138138
139139 /// Index into `node_storage`.
140 const Index = enum(u16) {
140 const Index = enum(u8) {
141141 _,
142142
143143 fn toParent(i: @This()) Parent {
......@@ -589,8 +589,6 @@ fn serialize(serialized_buffer: *Serialized.Buffer) Serialized {
589589 };
590590}
591591
592var ipc_metadata_len: u16 = 0;
593
594592const SavedMetadata = struct {
595593 ipc_fd: u16,
596594 main_index: u8,
......@@ -612,6 +610,9 @@ const SavedMetadata = struct {
612610 }
613611};
614612
613var ipc_metadata_len: u8 = 0;
614var remaining_read_trash_bytes: usize = 0;
615
615616fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buffer) usize {
616617 const ipc_metadata_copy = &serialized_buffer.ipc_metadata_copy;
617618 const ipc_metadata = &serialized_buffer.ipc_metadata;
......@@ -641,36 +642,43 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
641642 },
642643 };
643644 if (n == 0) break;
645 if (remaining_read_trash_bytes > 0) {
646 assert(bytes_read == 0);
647 if (remaining_read_trash_bytes >= n) {
648 remaining_read_trash_bytes -= n;
649 continue;
650 }
651 const src = pipe_buf[remaining_read_trash_bytes..n];
652 std.mem.copyForwards(u8, &pipe_buf, src);
653 remaining_read_trash_bytes = 0;
654 bytes_read = src.len;
655 continue;
656 }
644657 bytes_read += n;
645658 }
646659 // Ignore all but the last message on the pipe.
647 var input: []align(2) u8 = pipe_buf[0..bytes_read];
660 var input: []u8 = pipe_buf[0..bytes_read];
648661 if (input.len == 0) {
649662 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
650663 continue;
651664 }
652665
653666 const storage, const parents = while (true) {
654 if (input.len < 4) {
655 std.log.warn("short read: {d} out of 4 header bytes", .{input.len});
656 // TODO keep track of the short read to trash odd bytes with the next read
657 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
658 continue :main_loop;
659 }
660 const subtree_len = std.mem.readInt(u32, input[0..4], .little);
661 const expected_bytes = 4 + subtree_len * (@sizeOf(Node.Storage) + @sizeOf(Node.Parent));
667 const subtree_len: usize = input[0];
668 const expected_bytes = 1 + subtree_len * (@sizeOf(Node.Storage) + @sizeOf(Node.Parent));
662669 if (input.len < expected_bytes) {
663 std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len });
664 // TODO keep track of the short read to trash odd bytes with the next read
670 // Ignore short reads. We'll handle the next full message when it comes instead.
671 assert(remaining_read_trash_bytes == 0);
672 remaining_read_trash_bytes = expected_bytes - input.len;
665673 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
666674 continue :main_loop;
667675 }
668676 if (input.len > expected_bytes) {
669 input = @alignCast(input[expected_bytes..]);
677 input = input[expected_bytes..];
670678 continue;
671679 }
672 const storage_bytes = input[4..][0 .. subtree_len * @sizeOf(Node.Storage)];
673 const parents_bytes = input[4 + storage_bytes.len ..][0 .. subtree_len * @sizeOf(Node.Parent)];
680 const storage_bytes = input[1..][0 .. subtree_len * @sizeOf(Node.Storage)];
681 const parents_bytes = input[1 + storage_bytes.len ..][0 .. subtree_len * @sizeOf(Node.Parent)];
674682 break .{
675683 std.mem.bytesAsSlice(Node.Storage, storage_bytes),
676684 std.mem.bytesAsSlice(Node.Parent, parents_bytes),
......@@ -722,7 +730,7 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
722730 return serialized_len;
723731}
724732
725fn copyRoot(dest: *Node.Storage, src: *align(2) Node.Storage) void {
733fn copyRoot(dest: *Node.Storage, src: *align(1) Node.Storage) void {
726734 dest.* = .{
727735 .completed_count = src.completed_count,
728736 .estimated_total_count = src.estimated_total_count,
......@@ -937,7 +945,7 @@ fn write(buf: []const u8) void {
937945
938946fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {
939947 assert(serialized.parents.len == serialized.storage.len);
940 const serialized_len: u32 = @intCast(serialized.parents.len);
948 const serialized_len: u8 = @intCast(serialized.parents.len);
941949 const header = std.mem.asBytes(&serialized_len);
942950 const storage = std.mem.sliceAsBytes(serialized.storage);
943951 const parents = std.mem.sliceAsBytes(serialized.parents);
......@@ -977,7 +985,9 @@ fn maybeUpdateSize(resize_flag: bool) void {
977985 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
978986
979987 if (windows.kernel32.GetConsoleScreenBufferInfo(fd, &info) == windows.FALSE) {
980 @panic("TODO: handle this failure");
988 std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
989 global_progress.rows = 25;
990 global_progress.cols = 80;
981991 }
982992
983993 global_progress.rows = @intCast(info.dwSize.Y);
......@@ -995,7 +1005,9 @@ fn maybeUpdateSize(resize_flag: bool) void {
9951005 global_progress.rows = winsize.ws_row;
9961006 global_progress.cols = winsize.ws_col;
9971007 } else {
998 @panic("TODO: handle this failure");
1008 std.log.debug("failed to determine terminal size; using conservative guess 80x25", .{});
1009 global_progress.rows = 25;
1010 global_progress.cols = 80;
9991011 }
10001012 }
10011013}