| ... | @@ -458,13 +458,16 @@ pub fn start(options: Options) Node { | ... | @@ -458,13 +458,16 @@ pub fn start(options: Options) Node { |
| 458 | if (noop_impl) | 458 | if (noop_impl) |
| 459 | return Node.none; | 459 | return Node.none; |
| 460 | | 460 | |
| | 461 | const io = static_threaded_io.io(); |
| | 462 | |
| 461 | if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| { | 463 | if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| { |
| 462 | global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{ | 464 | global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{ |
| 463 | @as(posix.fd_t, switch (@typeInfo(posix.fd_t)) { | 465 | io, |
| | 466 | @as(Io.File, .{ .handle = switch (@typeInfo(posix.fd_t)) { |
| 464 | .int => ipc_fd, | 467 | .int => ipc_fd, |
| 465 | .pointer => @ptrFromInt(ipc_fd), | 468 | .pointer => @ptrFromInt(ipc_fd), |
| 466 | else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)), | 469 | else => @compileError("unsupported fd_t of " ++ @typeName(posix.fd_t)), |
| 467 | }), | 470 | } }), |
| 468 | }) catch |err| { | 471 | }) catch |err| { |
| 469 | std.log.warn("failed to spawn IPC thread for communicating progress to parent: {s}", .{@errorName(err)}); | 472 | std.log.warn("failed to spawn IPC thread for communicating progress to parent: {s}", .{@errorName(err)}); |
| 470 | return Node.none; | 473 | return Node.none; |
| ... | @@ -476,9 +479,9 @@ pub fn start(options: Options) Node { | ... | @@ -476,9 +479,9 @@ pub fn start(options: Options) Node { |
| 476 | } | 479 | } |
| 477 | const stderr: Io.File = .stderr(); | 480 | const stderr: Io.File = .stderr(); |
| 478 | global_progress.terminal = stderr; | 481 | global_progress.terminal = stderr; |
| 479 | if (stderr.enableAnsiEscapeCodes()) |_| { | 482 | if (stderr.enableAnsiEscapeCodes(io)) |_| { |
| 480 | global_progress.terminal_mode = .ansi_escape_codes; | 483 | global_progress.terminal_mode = .ansi_escape_codes; |
| 481 | } else |_| if (is_windows and stderr.isTty()) { | 484 | } else |_| if (is_windows and stderr.isTty(io)) { |
| 482 | global_progress.terminal_mode = TerminalMode{ .windows_api = .{ | 485 | global_progress.terminal_mode = TerminalMode{ .windows_api = .{ |
| 483 | .code_page = windows.kernel32.GetConsoleOutputCP(), | 486 | .code_page = windows.kernel32.GetConsoleOutputCP(), |
| 484 | } }; | 487 | } }; |
| ... | @@ -499,8 +502,8 @@ pub fn start(options: Options) Node { | ... | @@ -499,8 +502,8 @@ pub fn start(options: Options) Node { |
| 499 | | 502 | |
| 500 | if (switch (global_progress.terminal_mode) { | 503 | if (switch (global_progress.terminal_mode) { |
| 501 | .off => unreachable, // handled a few lines above | 504 | .off => unreachable, // handled a few lines above |
| 502 | .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{}), | 505 | .ansi_escape_codes => std.Thread.spawn(.{}, updateThreadRun, .{io}), |
| 503 | .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{}) else unreachable, | 506 | .windows_api => if (is_windows) std.Thread.spawn(.{}, windowsApiUpdateThreadRun, .{io}) else unreachable, |
| 504 | }) |thread| { | 507 | }) |thread| { |
| 505 | global_progress.update_thread = thread; | 508 | global_progress.update_thread = thread; |
| 506 | } else |err| { | 509 | } else |err| { |
| ... | @@ -531,7 +534,7 @@ fn wait(timeout_ns: u64) bool { | ... | @@ -531,7 +534,7 @@ fn wait(timeout_ns: u64) bool { |
| 531 | return resize_flag or (global_progress.cols == 0); | 534 | return resize_flag or (global_progress.cols == 0); |
| 532 | } | 535 | } |
| 533 | | 536 | |
| 534 | fn updateThreadRun() void { | 537 | fn updateThreadRun(io: Io) void { |
| 535 | // Store this data in the thread so that it does not need to be part of the | 538 | // Store this data in the thread so that it does not need to be part of the |
| 536 | // linker data of the main executable. | 539 | // linker data of the main executable. |
| 537 | var serialized_buffer: Serialized.Buffer = undefined; | 540 | var serialized_buffer: Serialized.Buffer = undefined; |
| ... | @@ -544,7 +547,7 @@ fn updateThreadRun() void { | ... | @@ -544,7 +547,7 @@ fn updateThreadRun() void { |
| 544 | const buffer, _ = computeRedraw(&serialized_buffer); | 547 | const buffer, _ = computeRedraw(&serialized_buffer); |
| 545 | if (stderr_mutex.tryLock()) { | 548 | if (stderr_mutex.tryLock()) { |
| 546 | defer stderr_mutex.unlock(); | 549 | defer stderr_mutex.unlock(); |
| 547 | write(buffer) catch return; | 550 | write(io, buffer) catch return; |
| 548 | global_progress.need_clear = true; | 551 | global_progress.need_clear = true; |
| 549 | } | 552 | } |
| 550 | } | 553 | } |
| ... | @@ -555,7 +558,7 @@ fn updateThreadRun() void { | ... | @@ -555,7 +558,7 @@ fn updateThreadRun() void { |
| 555 | if (@atomicLoad(bool, &global_progress.done, .monotonic)) { | 558 | if (@atomicLoad(bool, &global_progress.done, .monotonic)) { |
| 556 | stderr_mutex.lock(); | 559 | stderr_mutex.lock(); |
| 557 | defer stderr_mutex.unlock(); | 560 | defer stderr_mutex.unlock(); |
| 558 | return clearWrittenWithEscapeCodes() catch {}; | 561 | return clearWrittenWithEscapeCodes(io) catch {}; |
| 559 | } | 562 | } |
| 560 | | 563 | |
| 561 | maybeUpdateSize(resize_flag); | 564 | maybeUpdateSize(resize_flag); |
| ... | @@ -563,7 +566,7 @@ fn updateThreadRun() void { | ... | @@ -563,7 +566,7 @@ fn updateThreadRun() void { |
| 563 | const buffer, _ = computeRedraw(&serialized_buffer); | 566 | const buffer, _ = computeRedraw(&serialized_buffer); |
| 564 | if (stderr_mutex.tryLock()) { | 567 | if (stderr_mutex.tryLock()) { |
| 565 | defer stderr_mutex.unlock(); | 568 | defer stderr_mutex.unlock(); |
| 566 | write(buffer) catch return; | 569 | write(io, buffer) catch return; |
| 567 | global_progress.need_clear = true; | 570 | global_progress.need_clear = true; |
| 568 | } | 571 | } |
| 569 | } | 572 | } |
| ... | @@ -577,7 +580,7 @@ fn windowsApiWriteMarker() void { | ... | @@ -577,7 +580,7 @@ fn windowsApiWriteMarker() void { |
| 577 | _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null); | 580 | _ = windows.kernel32.WriteConsoleW(handle, &[_]u16{windows_api_start_marker}, 1, &num_chars_written, null); |
| 578 | } | 581 | } |
| 579 | | 582 | |
| 580 | fn windowsApiUpdateThreadRun() void { | 583 | fn windowsApiUpdateThreadRun(io: Io) void { |
| 581 | var serialized_buffer: Serialized.Buffer = undefined; | 584 | var serialized_buffer: Serialized.Buffer = undefined; |
| 582 | | 585 | |
| 583 | { | 586 | { |
| ... | @@ -589,7 +592,7 @@ fn windowsApiUpdateThreadRun() void { | ... | @@ -589,7 +592,7 @@ fn windowsApiUpdateThreadRun() void { |
| 589 | if (stderr_mutex.tryLock()) { | 592 | if (stderr_mutex.tryLock()) { |
| 590 | defer stderr_mutex.unlock(); | 593 | defer stderr_mutex.unlock(); |
| 591 | windowsApiWriteMarker(); | 594 | windowsApiWriteMarker(); |
| 592 | write(buffer) catch return; | 595 | write(io, buffer) catch return; |
| 593 | global_progress.need_clear = true; | 596 | global_progress.need_clear = true; |
| 594 | windowsApiMoveToMarker(nl_n) catch return; | 597 | windowsApiMoveToMarker(nl_n) catch return; |
| 595 | } | 598 | } |
| ... | @@ -611,7 +614,7 @@ fn windowsApiUpdateThreadRun() void { | ... | @@ -611,7 +614,7 @@ fn windowsApiUpdateThreadRun() void { |
| 611 | defer stderr_mutex.unlock(); | 614 | defer stderr_mutex.unlock(); |
| 612 | clearWrittenWindowsApi() catch return; | 615 | clearWrittenWindowsApi() catch return; |
| 613 | windowsApiWriteMarker(); | 616 | windowsApiWriteMarker(); |
| 614 | write(buffer) catch return; | 617 | write(io, buffer) catch return; |
| 615 | global_progress.need_clear = true; | 618 | global_progress.need_clear = true; |
| 616 | windowsApiMoveToMarker(nl_n) catch return; | 619 | windowsApiMoveToMarker(nl_n) catch return; |
| 617 | } | 620 | } |
| ... | @@ -624,8 +627,9 @@ fn windowsApiUpdateThreadRun() void { | ... | @@ -624,8 +627,9 @@ fn windowsApiUpdateThreadRun() void { |
| 624 | /// | 627 | /// |
| 625 | /// The lock is recursive; the same thread may hold the lock multiple times. | 628 | /// The lock is recursive; the same thread may hold the lock multiple times. |
| 626 | pub fn lockStdErr() void { | 629 | pub fn lockStdErr() void { |
| | 630 | const io = stderr_file_writer.io; |
| 627 | stderr_mutex.lock(); | 631 | stderr_mutex.lock(); |
| 628 | clearWrittenWithEscapeCodes() catch {}; | 632 | clearWrittenWithEscapeCodes(io) catch {}; |
| 629 | } | 633 | } |
| 630 | | 634 | |
| 631 | pub fn unlockStdErr() void { | 635 | pub fn unlockStdErr() void { |
| ... | @@ -636,10 +640,12 @@ pub fn unlockStdErr() void { | ... | @@ -636,10 +640,12 @@ pub fn unlockStdErr() void { |
| 636 | const stderr_writer: *Writer = &stderr_file_writer.interface; | 640 | const stderr_writer: *Writer = &stderr_file_writer.interface; |
| 637 | /// Protected by `stderr_mutex`. | 641 | /// Protected by `stderr_mutex`. |
| 638 | var stderr_file_writer: Io.File.Writer = .{ | 642 | var stderr_file_writer: Io.File.Writer = .{ |
| | 643 | .io = static_threaded_io.io(), |
| 639 | .interface = Io.File.Writer.initInterface(&.{}), | 644 | .interface = Io.File.Writer.initInterface(&.{}), |
| 640 | .file = if (is_windows) undefined else .stderr(), | 645 | .file = if (is_windows) undefined else .stderr(), |
| 641 | .mode = .streaming, | 646 | .mode = .streaming, |
| 642 | }; | 647 | }; |
| | 648 | var static_threaded_io: Io.Threaded = .init_single_threaded; |
| 643 | | 649 | |
| 644 | /// Allows the caller to freely write to the returned `Writer`, | 650 | /// Allows the caller to freely write to the returned `Writer`, |
| 645 | /// initialized with `buffer`, until `unlockStderrWriter` is called. | 651 | /// initialized with `buffer`, until `unlockStderrWriter` is called. |
| ... | @@ -647,9 +653,10 @@ var stderr_file_writer: Io.File.Writer = .{ | ... | @@ -647,9 +653,10 @@ var stderr_file_writer: Io.File.Writer = .{ |
| 647 | /// During the lock, any `std.Progress` information is cleared from the terminal. | 653 | /// During the lock, any `std.Progress` information is cleared from the terminal. |
| 648 | /// | 654 | /// |
| 649 | /// The lock is recursive; the same thread may hold the lock multiple times. | 655 | /// The lock is recursive; the same thread may hold the lock multiple times. |
| 650 | pub fn lockStderrWriter(buffer: []u8) *Writer { | 656 | pub fn lockStderrWriter(buffer: []u8) *Io.Writer { |
| | 657 | const io = stderr_file_writer.io; |
| 651 | stderr_mutex.lock(); | 658 | stderr_mutex.lock(); |
| 652 | clearWrittenWithEscapeCodes() catch {}; | 659 | clearWrittenWithEscapeCodes(io) catch {}; |
| 653 | if (is_windows) stderr_file_writer.file = .stderr(); | 660 | if (is_windows) stderr_file_writer.file = .stderr(); |
| 654 | stderr_writer.flush() catch {}; | 661 | stderr_writer.flush() catch {}; |
| 655 | stderr_writer.buffer = buffer; | 662 | stderr_writer.buffer = buffer; |
| ... | @@ -663,7 +670,7 @@ pub fn unlockStderrWriter() void { | ... | @@ -663,7 +670,7 @@ pub fn unlockStderrWriter() void { |
| 663 | stderr_mutex.unlock(); | 670 | stderr_mutex.unlock(); |
| 664 | } | 671 | } |
| 665 | | 672 | |
| 666 | fn ipcThreadRun(fd: posix.fd_t) anyerror!void { | 673 | fn ipcThreadRun(io: Io, file: Io.File) anyerror!void { |
| 667 | // Store this data in the thread so that it does not need to be part of the | 674 | // Store this data in the thread so that it does not need to be part of the |
| 668 | // linker data of the main executable. | 675 | // linker data of the main executable. |
| 669 | var serialized_buffer: Serialized.Buffer = undefined; | 676 | var serialized_buffer: Serialized.Buffer = undefined; |
| ... | @@ -675,7 +682,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { | ... | @@ -675,7 +682,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { |
| 675 | return; | 682 | return; |
| 676 | | 683 | |
| 677 | const serialized = serialize(&serialized_buffer); | 684 | const serialized = serialize(&serialized_buffer); |
| 678 | writeIpc(fd, serialized) catch |err| switch (err) { | 685 | writeIpc(io, file, serialized) catch |err| switch (err) { |
| 679 | error.BrokenPipe => return, | 686 | error.BrokenPipe => return, |
| 680 | }; | 687 | }; |
| 681 | } | 688 | } |
| ... | @@ -687,7 +694,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { | ... | @@ -687,7 +694,7 @@ fn ipcThreadRun(fd: posix.fd_t) anyerror!void { |
| 687 | return; | 694 | return; |
| 688 | | 695 | |
| 689 | const serialized = serialize(&serialized_buffer); | 696 | const serialized = serialize(&serialized_buffer); |
| 690 | writeIpc(fd, serialized) catch |err| switch (err) { | 697 | writeIpc(io, file, serialized) catch |err| switch (err) { |
| 691 | error.BrokenPipe => return, | 698 | error.BrokenPipe => return, |
| 692 | }; | 699 | }; |
| 693 | } | 700 | } |
| ... | @@ -786,11 +793,11 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { | ... | @@ -786,11 +793,11 @@ fn appendTreeSymbol(symbol: TreeSymbol, buf: []u8, start_i: usize) usize { |
| 786 | } | 793 | } |
| 787 | } | 794 | } |
| 788 | | 795 | |
| 789 | fn clearWrittenWithEscapeCodes() anyerror!void { | 796 | fn clearWrittenWithEscapeCodes(io: Io) anyerror!void { |
| 790 | if (noop_impl or !global_progress.need_clear) return; | 797 | if (noop_impl or !global_progress.need_clear) return; |
| 791 | | 798 | |
| 792 | global_progress.need_clear = false; | 799 | global_progress.need_clear = false; |
| 793 | try write(clear ++ progress_remove); | 800 | try write(io, clear ++ progress_remove); |
| 794 | } | 801 | } |
| 795 | | 802 | |
| 796 | /// U+25BA or ► | 803 | /// U+25BA or ► |
| ... | @@ -1417,13 +1424,13 @@ fn withinRowLimit(p: *Progress, nl_n: usize) bool { | ... | @@ -1417,13 +1424,13 @@ fn withinRowLimit(p: *Progress, nl_n: usize) bool { |
| 1417 | return nl_n + 2 < p.rows; | 1424 | return nl_n + 2 < p.rows; |
| 1418 | } | 1425 | } |
| 1419 | | 1426 | |
| 1420 | fn write(buf: []const u8) anyerror!void { | 1427 | fn write(io: Io, buf: []const u8) anyerror!void { |
| 1421 | try global_progress.terminal.writeAll(buf); | 1428 | try global_progress.terminal.writeStreamingAll(io, buf); |
| 1422 | } | 1429 | } |
| 1423 | | 1430 | |
| 1424 | var remaining_write_trash_bytes: usize = 0; | 1431 | var remaining_write_trash_bytes: usize = 0; |
| 1425 | | 1432 | |
| 1426 | fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { | 1433 | fn writeIpc(io: Io, file: Io.File, serialized: Serialized) error{BrokenPipe}!void { |
| 1427 | // Byteswap if necessary to ensure little endian over the pipe. This is | 1434 | // Byteswap if necessary to ensure little endian over the pipe. This is |
| 1428 | // needed because the parent or child process might be running in qemu. | 1435 | // needed because the parent or child process might be running in qemu. |
| 1429 | if (is_big_endian) for (serialized.storage) |*s| s.byteSwap(); | 1436 | if (is_big_endian) for (serialized.storage) |*s| s.byteSwap(); |
| ... | @@ -1434,11 +1441,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { | ... | @@ -1434,11 +1441,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { |
| 1434 | const storage = std.mem.sliceAsBytes(serialized.storage); | 1441 | const storage = std.mem.sliceAsBytes(serialized.storage); |
| 1435 | const parents = std.mem.sliceAsBytes(serialized.parents); | 1442 | const parents = std.mem.sliceAsBytes(serialized.parents); |
| 1436 | | 1443 | |
| 1437 | var vecs: [3]posix.iovec_const = .{ | 1444 | var vecs: [3][]const u8 = .{ header, storage, parents }; |
| 1438 | .{ .base = header.ptr, .len = header.len }, | | |
| 1439 | .{ .base = storage.ptr, .len = storage.len }, | | |
| 1440 | .{ .base = parents.ptr, .len = parents.len }, | | |
| 1441 | }; | | |
| 1442 | | 1445 | |
| 1443 | // Ensures the packet can fit in the pipe buffer. | 1446 | // Ensures the packet can fit in the pipe buffer. |
| 1444 | const upper_bound_msg_len = 1 + node_storage_buffer_len * @sizeOf(Node.Storage) + | 1447 | const upper_bound_msg_len = 1 + node_storage_buffer_len * @sizeOf(Node.Storage) + |
| ... | @@ -1449,7 +1452,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { | ... | @@ -1449,7 +1452,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { |
| 1449 | // We do this in a separate write call to give a better chance for the | 1452 | // We do this in a separate write call to give a better chance for the |
| 1450 | // writev below to be in a single packet. | 1453 | // writev below to be in a single packet. |
| 1451 | const n = @min(parents.len, remaining_write_trash_bytes); | 1454 | const n = @min(parents.len, remaining_write_trash_bytes); |
| 1452 | if (posix.write(fd, parents[0..n])) |written| { | 1455 | if (io.vtable.fileWriteStreaming(io.userdata, file, &.{}, &.{parents[0..n]}, 1)) |written| { |
| 1453 | remaining_write_trash_bytes -= written; | 1456 | remaining_write_trash_bytes -= written; |
| 1454 | continue; | 1457 | continue; |
| 1455 | } else |err| switch (err) { | 1458 | } else |err| switch (err) { |
| ... | @@ -1464,7 +1467,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { | ... | @@ -1464,7 +1467,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { |
| 1464 | | 1467 | |
| 1465 | // If this write would block we do not want to keep trying, but we need to | 1468 | // If this write would block we do not want to keep trying, but we need to |
| 1466 | // know if a partial message was written. | 1469 | // know if a partial message was written. |
| 1467 | if (writevNonblock(fd, &vecs)) |written| { | 1470 | if (writevNonblock(io, file, &vecs)) |written| { |
| 1468 | const total = header.len + storage.len + parents.len; | 1471 | const total = header.len + storage.len + parents.len; |
| 1469 | if (written < total) { | 1472 | if (written < total) { |
| 1470 | remaining_write_trash_bytes = total - written; | 1473 | remaining_write_trash_bytes = total - written; |
| ... | @@ -1479,7 +1482,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { | ... | @@ -1479,7 +1482,7 @@ fn writeIpc(fd: posix.fd_t, serialized: Serialized) error{BrokenPipe}!void { |
| 1479 | } | 1482 | } |
| 1480 | } | 1483 | } |
| 1481 | | 1484 | |
| 1482 | fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usize { | 1485 | fn writevNonblock(io: Io, file: Io.File, iov: [][]const u8) Io.File.Writer.Error!usize { |
| 1483 | var iov_index: usize = 0; | 1486 | var iov_index: usize = 0; |
| 1484 | var written: usize = 0; | 1487 | var written: usize = 0; |
| 1485 | var total_written: usize = 0; | 1488 | var total_written: usize = 0; |
| ... | @@ -1488,9 +1491,9 @@ fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usi | ... | @@ -1488,9 +1491,9 @@ fn writevNonblock(fd: posix.fd_t, iov: []posix.iovec_const) posix.WriteError!usi |
| 1488 | written >= iov[iov_index].len | 1491 | written >= iov[iov_index].len |
| 1489 | else | 1492 | else |
| 1490 | return total_written) : (iov_index += 1) written -= iov[iov_index].len; | 1493 | return total_written) : (iov_index += 1) written -= iov[iov_index].len; |
| 1491 | iov[iov_index].base += written; | 1494 | iov[iov_index].ptr += written; |
| 1492 | iov[iov_index].len -= written; | 1495 | iov[iov_index].len -= written; |
| 1493 | written = try posix.writev(fd, iov[iov_index..]); | 1496 | written = try io.vtable.fileWriteStreaming(io.userdata, file, &.{}, iov, 1); |
| 1494 | if (written == 0) return total_written; | 1497 | if (written == 0) return total_written; |
| 1495 | total_written += written; | 1498 | total_written += written; |
| 1496 | } | 1499 | } |