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 {...@@ -104,11 +104,11 @@ pub const Node = struct {
104 }104 }
105 };105 };
106106
107 const Parent = enum(u16) {107 const Parent = enum(u8) {
108 /// Unallocated storage.108 /// Unallocated storage.
109 unused = std.math.maxInt(u16) - 1,109 unused = std.math.maxInt(u8) - 1,
110 /// Indicates root node.110 /// Indicates root node.
111 none = std.math.maxInt(u16),111 none = std.math.maxInt(u8),
112 /// Index into `node_storage`.112 /// Index into `node_storage`.
113 _,113 _,
114114
...@@ -120,8 +120,8 @@ pub const Node = struct {...@@ -120,8 +120,8 @@ pub const Node = struct {
120 }120 }
121 };121 };
122122
123 const OptionalIndex = enum(u16) {123 const OptionalIndex = enum(u8) {
124 none = std.math.maxInt(u16),124 none = std.math.maxInt(u8),
125 /// Index into `node_storage`.125 /// Index into `node_storage`.
126 _,126 _,
127127
...@@ -137,7 +137,7 @@ pub const Node = struct {...@@ -137,7 +137,7 @@ pub const Node = struct {
137 };137 };
138138
139 /// Index into `node_storage`.139 /// Index into `node_storage`.
140 const Index = enum(u16) {140 const Index = enum(u8) {
141 _,141 _,
142142
143 fn toParent(i: @This()) Parent {143 fn toParent(i: @This()) Parent {
...@@ -589,8 +589,6 @@ fn serialize(serialized_buffer: *Serialized.Buffer) Serialized {...@@ -589,8 +589,6 @@ fn serialize(serialized_buffer: *Serialized.Buffer) Serialized {
589 };589 };
590}590}
591591
592var ipc_metadata_len: u16 = 0;
593
594const SavedMetadata = struct {592const SavedMetadata = struct {
595 ipc_fd: u16,593 ipc_fd: u16,
596 main_index: u8,594 main_index: u8,
...@@ -612,6 +610,9 @@ const SavedMetadata = struct {...@@ -612,6 +610,9 @@ const SavedMetadata = struct {
612 }610 }
613};611};
614612
613var ipc_metadata_len: u8 = 0;
614var remaining_read_trash_bytes: usize = 0;
615
615fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buffer) usize {616fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buffer) usize {
616 const ipc_metadata_copy = &serialized_buffer.ipc_metadata_copy;617 const ipc_metadata_copy = &serialized_buffer.ipc_metadata_copy;
617 const ipc_metadata = &serialized_buffer.ipc_metadata;618 const ipc_metadata = &serialized_buffer.ipc_metadata;
...@@ -641,36 +642,43 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff...@@ -641,36 +642,43 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
641 },642 },
642 };643 };
643 if (n == 0) break;644 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 }
644 bytes_read += n;657 bytes_read += n;
645 }658 }
646 // Ignore all but the last message on the pipe.659 // 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];
648 if (input.len == 0) {661 if (input.len == 0) {
649 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);662 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
650 continue;663 continue;
651 }664 }
652665
653 const storage, const parents = while (true) {666 const storage, const parents = while (true) {
654 if (input.len < 4) {667 const subtree_len: usize = input[0];
655 std.log.warn("short read: {d} out of 4 header bytes", .{input.len});668 const expected_bytes = 1 + subtree_len * (@sizeOf(Node.Storage) + @sizeOf(Node.Parent));
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));
662 if (input.len < expected_bytes) {669 if (input.len < expected_bytes) {
663 std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len });670 // Ignore short reads. We'll handle the next full message when it comes instead.
664 // TODO keep track of the short read to trash odd bytes with the next read671 assert(remaining_read_trash_bytes == 0);
672 remaining_read_trash_bytes = expected_bytes - input.len;
665 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);673 serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata);
666 continue :main_loop;674 continue :main_loop;
667 }675 }
668 if (input.len > expected_bytes) {676 if (input.len > expected_bytes) {
669 input = @alignCast(input[expected_bytes..]);677 input = input[expected_bytes..];
670 continue;678 continue;
671 }679 }
672 const storage_bytes = input[4..][0 .. subtree_len * @sizeOf(Node.Storage)];680 const storage_bytes = input[1..][0 .. subtree_len * @sizeOf(Node.Storage)];
673 const parents_bytes = input[4 + storage_bytes.len ..][0 .. subtree_len * @sizeOf(Node.Parent)];681 const parents_bytes = input[1 + storage_bytes.len ..][0 .. subtree_len * @sizeOf(Node.Parent)];
674 break .{682 break .{
675 std.mem.bytesAsSlice(Node.Storage, storage_bytes),683 std.mem.bytesAsSlice(Node.Storage, storage_bytes),
676 std.mem.bytesAsSlice(Node.Parent, parents_bytes),684 std.mem.bytesAsSlice(Node.Parent, parents_bytes),
...@@ -722,7 +730,7 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff...@@ -722,7 +730,7 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
722 return serialized_len;730 return serialized_len;
723}731}
724732
725fn copyRoot(dest: *Node.Storage, src: *align(2) Node.Storage) void {733fn copyRoot(dest: *Node.Storage, src: *align(1) Node.Storage) void {
726 dest.* = .{734 dest.* = .{
727 .completed_count = src.completed_count,735 .completed_count = src.completed_count,
728 .estimated_total_count = src.estimated_total_count,736 .estimated_total_count = src.estimated_total_count,
...@@ -937,7 +945,7 @@ fn write(buf: []const u8) void {...@@ -937,7 +945,7 @@ fn write(buf: []const u8) void {
937945
938fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {946fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void {
939 assert(serialized.parents.len == serialized.storage.len);947 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);
941 const header = std.mem.asBytes(&serialized_len);949 const header = std.mem.asBytes(&serialized_len);
942 const storage = std.mem.sliceAsBytes(serialized.storage);950 const storage = std.mem.sliceAsBytes(serialized.storage);
943 const parents = std.mem.sliceAsBytes(serialized.parents);951 const parents = std.mem.sliceAsBytes(serialized.parents);
...@@ -977,7 +985,9 @@ fn maybeUpdateSize(resize_flag: bool) void {...@@ -977,7 +985,9 @@ fn maybeUpdateSize(resize_flag: bool) void {
977 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;985 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
978986
979 if (windows.kernel32.GetConsoleScreenBufferInfo(fd, &info) == windows.FALSE) {987 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;
981 }991 }
982992
983 global_progress.rows = @intCast(info.dwSize.Y);993 global_progress.rows = @intCast(info.dwSize.Y);
...@@ -995,7 +1005,9 @@ fn maybeUpdateSize(resize_flag: bool) void {...@@ -995,7 +1005,9 @@ fn maybeUpdateSize(resize_flag: bool) void {
995 global_progress.rows = winsize.ws_row;1005 global_progress.rows = winsize.ws_row;
996 global_progress.cols = winsize.ws_col;1006 global_progress.cols = winsize.ws_col;
997 } else {1007 } 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;
999 }1011 }
1000 }1012 }
1001}1013}