authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-24 15:03:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-27 20:56:48-07:00
logca03c9c512f09baff0ea6e44ec665ec6310526c7
tree61c5afcf950499a6a0145e307e70583fcaee77e7
parent516366f78f00d7627b772c0fc8a41ba7747a4f61

std.Progress: fix race condition with IPC nodes

It stored some metadata into the canonical node storage data but that is a race condition because another thread recycles those nodes. Also, keep the parent name for empty child root node names.

1 files changed, 49 insertions(+), 40 deletions(-)

lib/std/Progress.zig+49-40
...@@ -86,17 +86,7 @@ pub const Node = struct {...@@ -86,17 +86,7 @@ pub const Node = struct {
86 name: [max_name_len]u8,86 name: [max_name_len]u8,
8787
88 fn getIpcFd(s: Storage) ?posix.fd_t {88 fn getIpcFd(s: Storage) ?posix.fd_t {
89 if (s.estimated_total_count != std.math.maxInt(u32))89 return if (s.estimated_total_count != std.math.maxInt(u32)) null else @bitCast(s.completed_count);
90 return null;
91
92 const low: u16 = @truncate(s.completed_count);
93 return low;
94 }
95
96 fn getMainStorageIndex(s: Storage) Node.Index {
97 assert(s.estimated_total_count == std.math.maxInt(u32));
98 const i: u16 = @truncate(s.completed_count >> 16);
99 return @enumFromInt(i);
100 }90 }
10191
102 fn setIpcFd(s: *Storage, fd: posix.fd_t) void {92 fn setIpcFd(s: *Storage, fd: posix.fd_t) void {
...@@ -538,14 +528,9 @@ fn serialize() Serialized {...@@ -538,14 +528,9 @@ fn serialize() Serialized {
538 @memcpy(&dest_storage.name, &storage_ptr.name);528 @memcpy(&dest_storage.name, &storage_ptr.name);
539 dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic);529 dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic);
540 dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .monotonic);530 dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .monotonic);
541
542 if (dest_storage.getIpcFd() != null) {
543 any_ipc = true;
544 dest_storage.completed_count |= @as(u32, @intCast(i)) << 16;
545 }
546
547 const end_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst);531 const end_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst);
548 if (begin_parent == end_parent) {532 if (begin_parent == end_parent) {
533 any_ipc = any_ipc or (dest_storage.getIpcFd() != null);
549 serialized_node_parents_buffer[serialized_len] = begin_parent;534 serialized_node_parents_buffer[serialized_len] = begin_parent;
550 serialized_node_map_buffer[i] = @enumFromInt(serialized_len);535 serialized_node_map_buffer[i] = @enumFromInt(serialized_len);
551 serialized_len += 1;536 serialized_len += 1;
...@@ -577,23 +562,25 @@ fn serialize() Serialized {...@@ -577,23 +562,25 @@ fn serialize() Serialized {
577562
578var parents_copy: [default_node_storage_buffer_len]Node.Parent = undefined;563var parents_copy: [default_node_storage_buffer_len]Node.Parent = undefined;
579var storage_copy: [default_node_storage_buffer_len]Node.Storage = undefined;564var storage_copy: [default_node_storage_buffer_len]Node.Storage = undefined;
565var ipc_metadata_copy: [default_node_storage_buffer_len]SavedMetadata = undefined;
580566
581const SavedMetadata = extern struct {567var ipc_metadata: [default_node_storage_buffer_len]SavedMetadata = undefined;
568var ipc_metadata_len: u16 = 0;
569
570const SavedMetadata = struct {
571 ipc_fd: u16,
572 main_index: u16,
582 start_index: u16,573 start_index: u16,
583 nodes_len: u16,574 nodes_len: u16,
584 main_index: u16,
585 flags: Flags,
586
587 const Flags = enum(u16) {
588 saved = std.math.maxInt(u16),
589 _,
590 };
591};575};
592576
593fn serializeIpc(start_serialized_len: usize) usize {577fn serializeIpc(start_serialized_len: usize) usize {
594 var serialized_len = start_serialized_len;578 var serialized_len = start_serialized_len;
595 var pipe_buf: [2 * 4096]u8 align(4) = undefined;579 var pipe_buf: [2 * 4096]u8 align(4) = undefined;
596580
581 const old_ipc_metadata = ipc_metadata_copy[0..ipc_metadata_len];
582 ipc_metadata_len = 0;
583
597 main_loop: for (584 main_loop: for (
598 serialized_node_parents_buffer[0..serialized_len],585 serialized_node_parents_buffer[0..serialized_len],
599 serialized_node_storage_buffer[0..serialized_len],586 serialized_node_storage_buffer[0..serialized_len],
...@@ -618,7 +605,7 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -618,7 +605,7 @@ fn serializeIpc(start_serialized_len: usize) usize {
618 // Ignore all but the last message on the pipe.605 // Ignore all but the last message on the pipe.
619 var input: []align(2) u8 = pipe_buf[0..bytes_read];606 var input: []align(2) u8 = pipe_buf[0..bytes_read];
620 if (input.len == 0) {607 if (input.len == 0) {
621 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index);608 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata);
622 continue;609 continue;
623 }610 }
624611
...@@ -626,7 +613,7 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -626,7 +613,7 @@ fn serializeIpc(start_serialized_len: usize) usize {
626 if (input.len < 4) {613 if (input.len < 4) {
627 std.log.warn("short read: {d} out of 4 header bytes", .{input.len});614 std.log.warn("short read: {d} out of 4 header bytes", .{input.len});
628 // TODO keep track of the short read to trash odd bytes with the next read615 // TODO keep track of the short read to trash odd bytes with the next read
629 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index);616 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata);
630 continue :main_loop;617 continue :main_loop;
631 }618 }
632 const subtree_len = std.mem.readInt(u32, input[0..4], .little);619 const subtree_len = std.mem.readInt(u32, input[0..4], .little);
...@@ -634,7 +621,7 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -634,7 +621,7 @@ fn serializeIpc(start_serialized_len: usize) usize {
634 if (input.len < expected_bytes) {621 if (input.len < expected_bytes) {
635 std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len });622 std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len });
636 // TODO keep track of the short read to trash odd bytes with the next read623 // TODO keep track of the short read to trash odd bytes with the next read
637 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index);624 serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata);
638 continue :main_loop;625 continue :main_loop;
639 }626 }
640 if (input.len > expected_bytes) {627 if (input.len > expected_bytes) {
...@@ -650,16 +637,16 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -650,16 +637,16 @@ fn serializeIpc(start_serialized_len: usize) usize {
650 };637 };
651638
652 // Remember in case the pipe is empty on next update.639 // Remember in case the pipe is empty on next update.
653 const real_storage: *Node.Storage = Node.storageByIndex(main_storage.getMainStorageIndex());640 ipc_metadata[ipc_metadata_len] = .{
654 @as(*SavedMetadata, @ptrCast(&real_storage.name)).* = .{641 .ipc_fd = @intCast(fd),
655 .start_index = @intCast(serialized_len),642 .start_index = @intCast(serialized_len),
656 .nodes_len = @intCast(parents.len),643 .nodes_len = @intCast(parents.len),
657 .main_index = @intCast(main_index),644 .main_index = @intCast(main_index),
658 .flags = .saved,
659 };645 };
646 ipc_metadata_len += 1;
660647
661 // Mount the root here.648 // Mount the root here.
662 main_storage.* = storage[0];649 copyRoot(main_storage, &storage[0]);
663650
664 // Copy the rest of the tree to the end.651 // Copy the rest of the tree to the end.
665 @memcpy(serialized_node_storage_buffer[serialized_len..][0 .. storage.len - 1], storage[1..]);652 @memcpy(serialized_node_storage_buffer[serialized_len..][0 .. storage.len - 1], storage[1..]);
...@@ -685,34 +672,56 @@ fn serializeIpc(start_serialized_len: usize) usize {...@@ -685,34 +672,56 @@ fn serializeIpc(start_serialized_len: usize) usize {
685 // Save a copy in case any pipes are empty on the next update.672 // Save a copy in case any pipes are empty on the next update.
686 @memcpy(parents_copy[0..serialized_len], serialized_node_parents_buffer[0..serialized_len]);673 @memcpy(parents_copy[0..serialized_len], serialized_node_parents_buffer[0..serialized_len]);
687 @memcpy(storage_copy[0..serialized_len], serialized_node_storage_buffer[0..serialized_len]);674 @memcpy(storage_copy[0..serialized_len], serialized_node_storage_buffer[0..serialized_len]);
675 @memcpy(ipc_metadata_copy[0..ipc_metadata_len], ipc_metadata[0..ipc_metadata_len]);
688676
689 return serialized_len;677 return serialized_len;
690}678}
691679
692fn useSavedIpcData(start_serialized_len: usize, main_storage: *Node.Storage, main_index: usize) usize {680fn copyRoot(dest: *Node.Storage, src: *align(2) Node.Storage) void {
693 const saved_metadata: *SavedMetadata = @ptrCast(&main_storage.name);681 dest.* = .{
694 if (saved_metadata.flags != .saved) {682 .completed_count = src.completed_count,
683 .estimated_total_count = src.estimated_total_count,
684 .name = if (src.name[0] == 0) dest.name else src.name,
685 };
686}
687
688fn findOld(ipc_fd: posix.fd_t, old_metadata: []const SavedMetadata) ?*const SavedMetadata {
689 for (old_metadata) |*m| {
690 if (m.ipc_fd == ipc_fd)
691 return m;
692 }
693 return null;
694}
695
696fn useSavedIpcData(
697 start_serialized_len: usize,
698 main_storage: *Node.Storage,
699 main_index: usize,
700 old_metadata: []const SavedMetadata,
701) usize {
702 const ipc_fd = main_storage.getIpcFd().?;
703 const saved_metadata = findOld(ipc_fd, old_metadata) orelse {
695 main_storage.completed_count = 0;704 main_storage.completed_count = 0;
696 main_storage.estimated_total_count = 0;705 main_storage.estimated_total_count = 0;
697 return start_serialized_len;706 return start_serialized_len;
698 }707 };
699708
700 const start_index = saved_metadata.start_index;709 const start_index = saved_metadata.start_index;
701 const nodes_len = saved_metadata.nodes_len;710 const nodes_len = saved_metadata.nodes_len;
702 const old_main_index = saved_metadata.main_index;711 const old_main_index = saved_metadata.main_index;
703712
704 const real_storage: *Node.Storage = Node.storageByIndex(main_storage.getMainStorageIndex());713 ipc_metadata[ipc_metadata_len] = .{
705 @as(*SavedMetadata, @ptrCast(&real_storage.name)).* = .{714 .ipc_fd = @intCast(ipc_fd),
706 .start_index = @intCast(start_serialized_len),715 .start_index = @intCast(start_serialized_len),
707 .nodes_len = nodes_len,716 .nodes_len = nodes_len,
708 .main_index = @intCast(main_index),717 .main_index = @intCast(main_index),
709 .flags = .saved,
710 };718 };
719 ipc_metadata_len += 1;
711720
712 const parents = parents_copy[start_index..][0 .. nodes_len - 1];721 const parents = parents_copy[start_index..][0 .. nodes_len - 1];
713 const storage = storage_copy[start_index..][0 .. nodes_len - 1];722 const storage = storage_copy[start_index..][0 .. nodes_len - 1];
714723
715 main_storage.* = storage_copy[old_main_index];724 copyRoot(main_storage, &storage_copy[old_main_index]);
716725
717 @memcpy(serialized_node_storage_buffer[start_serialized_len..][0..storage.len], storage);726 @memcpy(serialized_node_storage_buffer[start_serialized_len..][0..storage.len], storage);
718727