| ... | ... | @@ -280,7 +280,6 @@ var global_progress: Progress = .{ |
| 280 | 280 | .draw_buffer = undefined, |
| 281 | 281 | .done = false, |
| 282 | 282 | |
| 283 | | // TODO: make these configurable and avoid including the globals in .data if unused |
| 284 | 283 | .node_parents = &node_parents_buffer, |
| 285 | 284 | .node_storage = &node_storage_buffer, |
| 286 | 285 | .node_freelist = &node_freelist_buffer, |
| ... | ... | @@ -288,10 +287,10 @@ var global_progress: Progress = .{ |
| 288 | 287 | .node_end_index = 0, |
| 289 | 288 | }; |
| 290 | 289 | |
| 291 | | const default_node_storage_buffer_len = 200; |
| 292 | | var node_parents_buffer: [default_node_storage_buffer_len]Node.Parent = undefined; |
| 293 | | var node_storage_buffer: [default_node_storage_buffer_len]Node.Storage = undefined; |
| 294 | | var node_freelist_buffer: [default_node_storage_buffer_len]Node.OptionalIndex = undefined; |
| 290 | const node_storage_buffer_len = 200; |
| 291 | var node_parents_buffer: [node_storage_buffer_len]Node.Parent = undefined; |
| 292 | var node_storage_buffer: [node_storage_buffer_len]Node.Storage = undefined; |
| 293 | var node_freelist_buffer: [node_storage_buffer_len]Node.OptionalIndex = undefined; |
| 295 | 294 | |
| 296 | 295 | var default_draw_buffer: [4096]u8 = undefined; |
| 297 | 296 | |
| ... | ... | @@ -391,14 +390,21 @@ fn wait(timeout_ns: u64) bool { |
| 391 | 390 | } |
| 392 | 391 | |
| 393 | 392 | fn updateThreadRun() void { |
| 393 | // Store this data in the thread so that it does not need to be part of the |
| 394 | // linker data of the main executable. |
| 395 | var serialized_buffer: Serialized.Buffer = undefined; |
| 396 | |
| 394 | 397 | { |
| 395 | 398 | const resize_flag = wait(global_progress.initial_delay_ns); |
| 396 | 399 | maybeUpdateSize(resize_flag); |
| 397 | 400 | |
| 398 | | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) |
| 401 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) { |
| 402 | stderr_mutex.lock(); |
| 403 | defer stderr_mutex.unlock(); |
| 399 | 404 | return clearTerminal(); |
| 405 | } |
| 400 | 406 | |
| 401 | | const buffer = computeRedraw(); |
| 407 | const buffer = computeRedraw(&serialized_buffer); |
| 402 | 408 | if (stderr_mutex.tryLock()) { |
| 403 | 409 | defer stderr_mutex.unlock(); |
| 404 | 410 | write(buffer); |
| ... | ... | @@ -409,10 +415,13 @@ fn updateThreadRun() void { |
| 409 | 415 | const resize_flag = wait(global_progress.refresh_rate_ns); |
| 410 | 416 | maybeUpdateSize(resize_flag); |
| 411 | 417 | |
| 412 | | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) |
| 418 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) { |
| 419 | stderr_mutex.lock(); |
| 420 | defer stderr_mutex.unlock(); |
| 413 | 421 | return clearTerminal(); |
| 422 | } |
| 414 | 423 | |
| 415 | | const buffer = computeRedraw(); |
| 424 | const buffer = computeRedraw(&serialized_buffer); |
| 416 | 425 | if (stderr_mutex.tryLock()) { |
| 417 | 426 | defer stderr_mutex.unlock(); |
| 418 | 427 | write(buffer); |
| ... | ... | @@ -433,13 +442,17 @@ pub fn unlockStdErr() void { |
| 433 | 442 | } |
| 434 | 443 | |
| 435 | 444 | fn ipcThreadRun(fd: posix.fd_t) anyerror!void { |
| 445 | // Store this data in the thread so that it does not need to be part of the |
| 446 | // linker data of the main executable. |
| 447 | var serialized_buffer: Serialized.Buffer = undefined; |
| 448 | |
| 436 | 449 | { |
| 437 | 450 | _ = wait(global_progress.initial_delay_ns); |
| 438 | 451 | |
| 439 | 452 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) |
| 440 | 453 | return; |
| 441 | 454 | |
| 442 | | const serialized = serialize(); |
| 455 | const serialized = serialize(&serialized_buffer); |
| 443 | 456 | writeIpc(fd, serialized) catch |err| switch (err) { |
| 444 | 457 | error.BrokenPipe => return, |
| 445 | 458 | }; |
| ... | ... | @@ -451,7 +464,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { |
| 451 | 464 | if (@atomicLoad(bool, &global_progress.done, .seq_cst)) |
| 452 | 465 | return clearTerminal(); |
| 453 | 466 | |
| 454 | | const serialized = serialize(); |
| 467 | const serialized = serialize(&serialized_buffer); |
| 455 | 468 | writeIpc(fd, serialized) catch |err| switch (err) { |
| 456 | 469 | error.BrokenPipe => return, |
| 457 | 470 | }; |
| ... | ... | @@ -511,17 +524,18 @@ const Children = struct { |
| 511 | 524 | sibling: Node.OptionalIndex, |
| 512 | 525 | }; |
| 513 | 526 | |
| 514 | | // TODO make this configurable |
| 515 | | var serialized_node_parents_buffer: [default_node_storage_buffer_len]Node.Parent = undefined; |
| 516 | | var serialized_node_storage_buffer: [default_node_storage_buffer_len]Node.Storage = undefined; |
| 517 | | var serialized_node_map_buffer: [default_node_storage_buffer_len]Node.Index = undefined; |
| 518 | | |
| 519 | 527 | const Serialized = struct { |
| 520 | 528 | parents: []Node.Parent, |
| 521 | 529 | storage: []Node.Storage, |
| 530 | |
| 531 | const Buffer = struct { |
| 532 | parents: [node_storage_buffer_len]Node.Parent, |
| 533 | storage: [node_storage_buffer_len]Node.Storage, |
| 534 | map: [node_storage_buffer_len]Node.Index, |
| 535 | }; |
| 522 | 536 | }; |
| 523 | 537 | |
| 524 | | fn serialize() Serialized { |
| 538 | fn serialize(serialized_buffer: *Serialized.Buffer) Serialized { |
| 525 | 539 | var serialized_len: usize = 0; |
| 526 | 540 | var any_ipc = false; |
| 527 | 541 | |
| ... | ... | @@ -533,15 +547,15 @@ fn serialize() Serialized { |
| 533 | 547 | for (node_parents, node_storage, 0..) |*parent_ptr, *storage_ptr, i| { |
| 534 | 548 | var begin_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst); |
| 535 | 549 | while (begin_parent != .unused) { |
| 536 | | const dest_storage = &serialized_node_storage_buffer[serialized_len]; |
| 550 | const dest_storage = &serialized_buffer.storage[serialized_len]; |
| 537 | 551 | @memcpy(&dest_storage.name, &storage_ptr.name); |
| 538 | 552 | dest_storage.completed_count = @atomicLoad(u32, &storage_ptr.completed_count, .monotonic); |
| 539 | 553 | dest_storage.estimated_total_count = @atomicLoad(u32, &storage_ptr.estimated_total_count, .monotonic); |
| 540 | 554 | const end_parent = @atomicLoad(Node.Parent, parent_ptr, .seq_cst); |
| 541 | 555 | if (begin_parent == end_parent) { |
| 542 | 556 | any_ipc = any_ipc or (dest_storage.getIpcFd() != null); |
| 543 | | serialized_node_parents_buffer[serialized_len] = begin_parent; |
| 544 | | serialized_node_map_buffer[i] = @enumFromInt(serialized_len); |
| 557 | serialized_buffer.parents[serialized_len] = begin_parent; |
| 558 | serialized_buffer.map[i] = @enumFromInt(serialized_len); |
| 545 | 559 | serialized_len += 1; |
| 546 | 560 | break; |
| 547 | 561 | } |
| ... | ... | @@ -551,29 +565,29 @@ fn serialize() Serialized { |
| 551 | 565 | } |
| 552 | 566 | |
| 553 | 567 | // Remap parents to point inside serialized arrays. |
| 554 | | for (serialized_node_parents_buffer[0..serialized_len]) |*parent| { |
| 568 | for (serialized_buffer.parents[0..serialized_len]) |*parent| { |
| 555 | 569 | parent.* = switch (parent.*) { |
| 556 | 570 | .unused => unreachable, |
| 557 | 571 | .none => .none, |
| 558 | | _ => |p| serialized_node_map_buffer[@intFromEnum(p)].toParent(), |
| 572 | _ => |p| serialized_buffer.map[@intFromEnum(p)].toParent(), |
| 559 | 573 | }; |
| 560 | 574 | } |
| 561 | 575 | |
| 562 | 576 | // Find nodes which correspond to child processes. |
| 563 | 577 | if (any_ipc) |
| 564 | | serialized_len = serializeIpc(serialized_len); |
| 578 | serialized_len = serializeIpc(serialized_len, serialized_buffer); |
| 565 | 579 | |
| 566 | 580 | return .{ |
| 567 | | .parents = serialized_node_parents_buffer[0..serialized_len], |
| 568 | | .storage = serialized_node_storage_buffer[0..serialized_len], |
| 581 | .parents = serialized_buffer.parents[0..serialized_len], |
| 582 | .storage = serialized_buffer.storage[0..serialized_len], |
| 569 | 583 | }; |
| 570 | 584 | } |
| 571 | 585 | |
| 572 | | var parents_copy: [default_node_storage_buffer_len]Node.Parent = undefined; |
| 573 | | var storage_copy: [default_node_storage_buffer_len]Node.Storage = undefined; |
| 574 | | var ipc_metadata_copy: [default_node_storage_buffer_len]SavedMetadata = undefined; |
| 586 | var parents_copy: [node_storage_buffer_len]Node.Parent = undefined; |
| 587 | var storage_copy: [node_storage_buffer_len]Node.Storage = undefined; |
| 588 | var ipc_metadata_copy: [node_storage_buffer_len]SavedMetadata = undefined; |
| 575 | 589 | |
| 576 | | var ipc_metadata: [default_node_storage_buffer_len]SavedMetadata = undefined; |
| 590 | var ipc_metadata: [node_storage_buffer_len]SavedMetadata = undefined; |
| 577 | 591 | var ipc_metadata_len: u16 = 0; |
| 578 | 592 | |
| 579 | 593 | const SavedMetadata = struct { |
| ... | ... | @@ -597,7 +611,7 @@ const SavedMetadata = struct { |
| 597 | 611 | } |
| 598 | 612 | }; |
| 599 | 613 | |
| 600 | | fn serializeIpc(start_serialized_len: usize) usize { |
| 614 | fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buffer) usize { |
| 601 | 615 | var serialized_len = start_serialized_len; |
| 602 | 616 | var pipe_buf: [2 * 4096]u8 align(4) = undefined; |
| 603 | 617 | |
| ... | ... | @@ -605,8 +619,8 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 605 | 619 | ipc_metadata_len = 0; |
| 606 | 620 | |
| 607 | 621 | main_loop: for ( |
| 608 | | serialized_node_parents_buffer[0..serialized_len], |
| 609 | | serialized_node_storage_buffer[0..serialized_len], |
| 622 | serialized_buffer.parents[0..serialized_len], |
| 623 | serialized_buffer.storage[0..serialized_len], |
| 610 | 624 | 0.., |
| 611 | 625 | ) |main_parent, *main_storage, main_index| { |
| 612 | 626 | if (main_parent == .unused) continue; |
| ... | ... | @@ -628,7 +642,7 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 628 | 642 | // Ignore all but the last message on the pipe. |
| 629 | 643 | var input: []align(2) u8 = pipe_buf[0..bytes_read]; |
| 630 | 644 | if (input.len == 0) { |
| 631 | | serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata); |
| 645 | serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata); |
| 632 | 646 | continue; |
| 633 | 647 | } |
| 634 | 648 | |
| ... | ... | @@ -636,7 +650,7 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 636 | 650 | if (input.len < 4) { |
| 637 | 651 | std.log.warn("short read: {d} out of 4 header bytes", .{input.len}); |
| 638 | 652 | // TODO keep track of the short read to trash odd bytes with the next read |
| 639 | | serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata); |
| 653 | serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata); |
| 640 | 654 | continue :main_loop; |
| 641 | 655 | } |
| 642 | 656 | const subtree_len = std.mem.readInt(u32, input[0..4], .little); |
| ... | ... | @@ -644,7 +658,7 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 644 | 658 | if (input.len < expected_bytes) { |
| 645 | 659 | std.log.warn("short read: {d} out of {d} ({d} nodes)", .{ input.len, expected_bytes, subtree_len }); |
| 646 | 660 | // TODO keep track of the short read to trash odd bytes with the next read |
| 647 | | serialized_len = useSavedIpcData(serialized_len, main_storage, main_index, old_ipc_metadata); |
| 661 | serialized_len = useSavedIpcData(serialized_len, serialized_buffer, main_storage, main_index, old_ipc_metadata); |
| 648 | 662 | continue :main_loop; |
| 649 | 663 | } |
| 650 | 664 | if (input.len > expected_bytes) { |
| ... | ... | @@ -672,12 +686,12 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 672 | 686 | copyRoot(main_storage, &storage[0]); |
| 673 | 687 | |
| 674 | 688 | // Copy the rest of the tree to the end. |
| 675 | | @memcpy(serialized_node_storage_buffer[serialized_len..][0 .. storage.len - 1], storage[1..]); |
| 689 | @memcpy(serialized_buffer.storage[serialized_len..][0 .. storage.len - 1], storage[1..]); |
| 676 | 690 | |
| 677 | 691 | // Patch up parent pointers taking into account how the subtree is mounted. |
| 678 | | serialized_node_parents_buffer[serialized_len] = .none; |
| 692 | serialized_buffer.parents[serialized_len] = .none; |
| 679 | 693 | |
| 680 | | for (serialized_node_parents_buffer[serialized_len..][0 .. parents.len - 1], parents[1..]) |*dest, p| { |
| 694 | for (serialized_buffer.parents[serialized_len..][0 .. parents.len - 1], parents[1..]) |*dest, p| { |
| 681 | 695 | dest.* = switch (p) { |
| 682 | 696 | // Fix bad data so the rest of the code does not see `unused`. |
| 683 | 697 | .none, .unused => .none, |
| ... | ... | @@ -693,8 +707,8 @@ fn serializeIpc(start_serialized_len: usize) usize { |
| 693 | 707 | } |
| 694 | 708 | |
| 695 | 709 | // Save a copy in case any pipes are empty on the next update. |
| 696 | | @memcpy(parents_copy[0..serialized_len], serialized_node_parents_buffer[0..serialized_len]); |
| 697 | | @memcpy(storage_copy[0..serialized_len], serialized_node_storage_buffer[0..serialized_len]); |
| 710 | @memcpy(parents_copy[0..serialized_len], serialized_buffer.parents[0..serialized_len]); |
| 711 | @memcpy(storage_copy[0..serialized_len], serialized_buffer.storage[0..serialized_len]); |
| 698 | 712 | @memcpy(ipc_metadata_copy[0..ipc_metadata_len], ipc_metadata[0..ipc_metadata_len]); |
| 699 | 713 | |
| 700 | 714 | return serialized_len; |
| ... | ... | @@ -718,6 +732,7 @@ fn findOld(ipc_fd: posix.fd_t, old_metadata: []const SavedMetadata) ?*const Save |
| 718 | 732 | |
| 719 | 733 | fn useSavedIpcData( |
| 720 | 734 | start_serialized_len: usize, |
| 735 | serialized_buffer: *Serialized.Buffer, |
| 721 | 736 | main_storage: *Node.Storage, |
| 722 | 737 | main_index: usize, |
| 723 | 738 | old_metadata: []const SavedMetadata, |
| ... | ... | @@ -746,9 +761,9 @@ fn useSavedIpcData( |
| 746 | 761 | |
| 747 | 762 | copyRoot(main_storage, &storage_copy[old_main_index]); |
| 748 | 763 | |
| 749 | | @memcpy(serialized_node_storage_buffer[start_serialized_len..][0..storage.len], storage); |
| 764 | @memcpy(serialized_buffer.storage[start_serialized_len..][0..storage.len], storage); |
| 750 | 765 | |
| 751 | | for (serialized_node_parents_buffer[start_serialized_len..][0..parents.len], parents) |*dest, p| { |
| 766 | for (serialized_buffer.parents[start_serialized_len..][0..parents.len], parents) |*dest, p| { |
| 752 | 767 | dest.* = switch (p) { |
| 753 | 768 | .none, .unused => .none, |
| 754 | 769 | _ => |prev| @enumFromInt(if (@intFromEnum(prev) == old_main_index) |
| ... | ... | @@ -761,14 +776,14 @@ fn useSavedIpcData( |
| 761 | 776 | return start_serialized_len + storage.len; |
| 762 | 777 | } |
| 763 | 778 | |
| 764 | | fn computeRedraw() []u8 { |
| 765 | | const serialized = serialize(); |
| 779 | fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 { |
| 780 | const serialized = serialize(serialized_buffer); |
| 766 | 781 | |
| 767 | 782 | // Now we can analyze our copy of the graph without atomics, reconstructing |
| 768 | 783 | // children lists which do not exist in the canonical data. These are |
| 769 | 784 | // needed for tree traversal below. |
| 770 | 785 | |
| 771 | | var children_buffer: [default_node_storage_buffer_len]Children = undefined; |
| 786 | var children_buffer: [node_storage_buffer_len]Children = undefined; |
| 772 | 787 | const children = children_buffer[0..serialized.parents.len]; |
| 773 | 788 | |
| 774 | 789 | @memset(children, .{ .child = .none, .sibling = .none }); |