| author | |
| committer | |
| log | b8955a2e0aea692b6c76dc39962b3620649b6e1f |
| tree | 6c3e465eb83a9cceefdfd126ff932056cb4dfefc |
| parent | bc8e1a74c514e487842c03ab32d7f6e49f42c529 |
9 files changed, 387 insertions(+), 380 deletions(-)
lib/std/Build/Fuzz/WebServer.zig+9-17| ... | ... | @@ -273,21 +273,17 @@ fn buildWasmBinary( |
| 273 | 273 | try sendMessage(child.stdin.?, .update); |
| 274 | 274 | try sendMessage(child.stdin.?, .exit); |
| 275 | 275 | |
| 276 | const Header = std.zig.Server.Message.Header; | |
| 277 | 276 | var result: ?Path = null; |
| 278 | 277 | var result_error_bundle = std.zig.ErrorBundle.empty; |
| 279 | 278 | |
| 280 | const stdout = poller.fifo(.stdout); | |
| 279 | const stdout = poller.reader(.stdout); | |
| 281 | 280 | |
| 282 | 281 | poll: while (true) { |
| 283 | while (stdout.readableLength() < @sizeOf(Header)) { | |
| 284 | if (!(try poller.poll())) break :poll; | |
| 285 | } | |
| 286 | const header = stdout.reader().readStruct(Header) catch unreachable; | |
| 287 | while (stdout.readableLength() < header.bytes_len) { | |
| 288 | if (!(try poller.poll())) break :poll; | |
| 289 | } | |
| 290 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 282 | const Header = std.zig.Server.Message.Header; | |
| 283 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; | |
| 284 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 285 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 286 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 291 | 287 | |
| 292 | 288 | switch (header.tag) { |
| 293 | 289 | .zig_version => { |
| ... | ... | @@ -325,15 +321,11 @@ fn buildWasmBinary( |
| 325 | 321 | }, |
| 326 | 322 | else => {}, // ignore other messages |
| 327 | 323 | } |
| 328 | ||
| 329 | stdout.discard(body.len); | |
| 330 | 324 | } |
| 331 | 325 | |
| 332 | const stderr = poller.fifo(.stderr); | |
| 333 | if (stderr.readableLength() > 0) { | |
| 334 | const owned_stderr = try stderr.toOwnedSlice(); | |
| 335 | defer gpa.free(owned_stderr); | |
| 336 | std.debug.print("{s}", .{owned_stderr}); | |
| 326 | const stderr_contents = try poller.toOwnedSlice(.stderr); | |
| 327 | if (stderr_contents.len > 0) { | |
| 328 | std.debug.print("{s}", .{stderr_contents}); | |
| 337 | 329 | } |
| 338 | 330 | |
| 339 | 331 | // Send EOF to stdin. |
lib/std/Build/Step.zig+25-34| ... | ... | @@ -286,7 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T { |
| 286 | 286 | } |
| 287 | 287 | |
| 288 | 288 | /// For debugging purposes, prints identifying information about this Step. |
| 289 | pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void { | |
| 289 | pub fn dump(step: *Step, w: *std.Io.Writer, tty_config: std.Io.tty.Config) void { | |
| 290 | 290 | const debug_info = std.debug.getSelfDebugInfo() catch |err| { |
| 291 | 291 | w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ |
| 292 | 292 | @errorName(err), |
| ... | ... | @@ -359,7 +359,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO |
| 359 | 359 | |
| 360 | 360 | pub const ZigProcess = struct { |
| 361 | 361 | child: std.process.Child, |
| 362 | poller: std.io.Poller(StreamEnum), | |
| 362 | poller: std.Io.Poller(StreamEnum), | |
| 363 | 363 | progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void, |
| 364 | 364 | |
| 365 | 365 | pub const StreamEnum = enum { stdout, stderr }; |
| ... | ... | @@ -428,7 +428,7 @@ pub fn evalZigProcess( |
| 428 | 428 | const zp = try gpa.create(ZigProcess); |
| 429 | 429 | zp.* = .{ |
| 430 | 430 | .child = child, |
| 431 | .poller = std.io.poll(gpa, ZigProcess.StreamEnum, .{ | |
| 431 | .poller = std.Io.poll(gpa, ZigProcess.StreamEnum, .{ | |
| 432 | 432 | .stdout = child.stdout.?, |
| 433 | 433 | .stderr = child.stderr.?, |
| 434 | 434 | }), |
| ... | ... | @@ -508,20 +508,16 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { |
| 508 | 508 | try sendMessage(zp.child.stdin.?, .update); |
| 509 | 509 | if (!watch) try sendMessage(zp.child.stdin.?, .exit); |
| 510 | 510 | |
| 511 | const Header = std.zig.Server.Message.Header; | |
| 512 | 511 | var result: ?Path = null; |
| 513 | 512 | |
| 514 | const stdout = zp.poller.fifo(.stdout); | |
| 513 | const stdout = zp.poller.reader(.stdout); | |
| 515 | 514 | |
| 516 | 515 | poll: while (true) { |
| 517 | while (stdout.readableLength() < @sizeOf(Header)) { | |
| 518 | if (!(try zp.poller.poll())) break :poll; | |
| 519 | } | |
| 520 | const header = stdout.reader().readStruct(Header) catch unreachable; | |
| 521 | while (stdout.readableLength() < header.bytes_len) { | |
| 522 | if (!(try zp.poller.poll())) break :poll; | |
| 523 | } | |
| 524 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 516 | const Header = std.zig.Server.Message.Header; | |
| 517 | while (stdout.buffered().len < @sizeOf(Header)) if (!try zp.poller.poll()) break :poll; | |
| 518 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 519 | while (stdout.buffered().len < header.bytes_len) if (!try zp.poller.poll()) break :poll; | |
| 520 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 525 | 521 | |
| 526 | 522 | switch (header.tag) { |
| 527 | 523 | .zig_version => { |
| ... | ... | @@ -547,11 +543,8 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { |
| 547 | 543 | .string_bytes = try arena.dupe(u8, string_bytes), |
| 548 | 544 | .extra = extra_array, |
| 549 | 545 | }; |
| 550 | if (watch) { | |
| 551 | // This message indicates the end of the update. | |
| 552 | stdout.discard(body.len); | |
| 553 | break; | |
| 554 | } | |
| 546 | // This message indicates the end of the update. | |
| 547 | if (watch) break :poll; | |
| 555 | 548 | }, |
| 556 | 549 | .emit_digest => { |
| 557 | 550 | const EmitDigest = std.zig.Server.Message.EmitDigest; |
| ... | ... | @@ -611,15 +604,13 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { |
| 611 | 604 | }, |
| 612 | 605 | else => {}, // ignore other messages |
| 613 | 606 | } |
| 614 | ||
| 615 | stdout.discard(body.len); | |
| 616 | 607 | } |
| 617 | 608 | |
| 618 | 609 | s.result_duration_ns = timer.read(); |
| 619 | 610 | |
| 620 | const stderr = zp.poller.fifo(.stderr); | |
| 621 | if (stderr.readableLength() > 0) { | |
| 622 | try s.result_error_msgs.append(arena, try stderr.toOwnedSlice()); | |
| 611 | const stderr_contents = try zp.poller.toOwnedSlice(.stderr); | |
| 612 | if (stderr_contents.len > 0) { | |
| 613 | try s.result_error_msgs.append(arena, try arena.dupe(u8, stderr_contents)); | |
| 623 | 614 | } |
| 624 | 615 | |
| 625 | 616 | return result; |
| ... | ... | @@ -736,7 +727,7 @@ pub fn allocPrintCmd2( |
| 736 | 727 | argv: []const []const u8, |
| 737 | 728 | ) Allocator.Error![]u8 { |
| 738 | 729 | const shell = struct { |
| 739 | fn escape(writer: anytype, string: []const u8, is_argv0: bool) !void { | |
| 730 | fn escape(writer: *std.Io.Writer, string: []const u8, is_argv0: bool) !void { | |
| 740 | 731 | for (string) |c| { |
| 741 | 732 | if (switch (c) { |
| 742 | 733 | else => true, |
| ... | ... | @@ -770,9 +761,9 @@ pub fn allocPrintCmd2( |
| 770 | 761 | } |
| 771 | 762 | }; |
| 772 | 763 | |
| 773 | var buf: std.ArrayListUnmanaged(u8) = .empty; | |
| 774 | const writer = buf.writer(arena); | |
| 775 | if (opt_cwd) |cwd| try writer.print("cd {s} && ", .{cwd}); | |
| 764 | var aw: std.Io.Writer.Allocating = .init(arena); | |
| 765 | const writer = &aw.writer; | |
| 766 | if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory; | |
| 776 | 767 | if (opt_env) |env| { |
| 777 | 768 | const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena); |
| 778 | 769 | var it = env.iterator(); |
| ... | ... | @@ -782,17 +773,17 @@ pub fn allocPrintCmd2( |
| 782 | 773 | if (process_env_map.get(key)) |process_value| { |
| 783 | 774 | if (std.mem.eql(u8, value, process_value)) continue; |
| 784 | 775 | } |
| 785 | try writer.print("{s}=", .{key}); | |
| 786 | try shell.escape(writer, value, false); | |
| 787 | try writer.writeByte(' '); | |
| 776 | writer.print("{s}=", .{key}) catch return error.OutOfMemory; | |
| 777 | shell.escape(writer, value, false) catch return error.OutOfMemory; | |
| 778 | writer.writeByte(' ') catch return error.OutOfMemory; | |
| 788 | 779 | } |
| 789 | 780 | } |
| 790 | try shell.escape(writer, argv[0], true); | |
| 781 | shell.escape(writer, argv[0], true) catch return error.OutOfMemory; | |
| 791 | 782 | for (argv[1..]) |arg| { |
| 792 | try writer.writeByte(' '); | |
| 793 | try shell.escape(writer, arg, false); | |
| 783 | writer.writeByte(' ') catch return error.OutOfMemory; | |
| 784 | shell.escape(writer, arg, false) catch return error.OutOfMemory; | |
| 794 | 785 | } |
| 795 | return buf.toOwnedSlice(arena); | |
| 786 | return aw.toOwnedSlice(); | |
| 796 | 787 | } |
| 797 | 788 | |
| 798 | 789 | /// Prefer `cacheHitAndWatch` unless you already added watch inputs |
lib/std/Build/Step/Run.zig+44-34| ... | ... | @@ -73,9 +73,12 @@ skip_foreign_checks: bool, |
| 73 | 73 | /// external executor (such as qemu) but not fail if the executor is unavailable. |
| 74 | 74 | failing_to_execute_foreign_is_an_error: bool, |
| 75 | 75 | |
| 76 | /// Deprecated in favor of `stdio_limit`. | |
| 77 | max_stdio_size: usize, | |
| 78 | ||
| 76 | 79 | /// If stderr or stdout exceeds this amount, the child process is killed and |
| 77 | 80 | /// the step fails. |
| 78 | max_stdio_size: usize, | |
| 81 | stdio_limit: std.Io.Limit, | |
| 79 | 82 | |
| 80 | 83 | captured_stdout: ?*Output, |
| 81 | 84 | captured_stderr: ?*Output, |
| ... | ... | @@ -186,6 +189,7 @@ pub fn create(owner: *std.Build, name: []const u8) *Run { |
| 186 | 189 | .skip_foreign_checks = false, |
| 187 | 190 | .failing_to_execute_foreign_is_an_error = true, |
| 188 | 191 | .max_stdio_size = 10 * 1024 * 1024, |
| 192 | .stdio_limit = .unlimited, | |
| 189 | 193 | .captured_stdout = null, |
| 190 | 194 | .captured_stderr = null, |
| 191 | 195 | .dep_output_file = null, |
| ... | ... | @@ -1011,7 +1015,7 @@ fn populateGeneratedPaths( |
| 1011 | 1015 | } |
| 1012 | 1016 | } |
| 1013 | 1017 | |
| 1014 | fn formatTerm(term: ?std.process.Child.Term, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 1018 | fn formatTerm(term: ?std.process.Child.Term, w: *std.Io.Writer) std.Io.Writer.Error!void { | |
| 1015 | 1019 | if (term) |t| switch (t) { |
| 1016 | 1020 | .Exited => |code| try w.print("exited with code {d}", .{code}), |
| 1017 | 1021 | .Signal => |sig| try w.print("terminated with signal {d}", .{sig}), |
| ... | ... | @@ -1500,7 +1504,7 @@ fn evalZigTest( |
| 1500 | 1504 | const gpa = run.step.owner.allocator; |
| 1501 | 1505 | const arena = run.step.owner.allocator; |
| 1502 | 1506 | |
| 1503 | var poller = std.io.poll(gpa, enum { stdout, stderr }, .{ | |
| 1507 | var poller = std.Io.poll(gpa, enum { stdout, stderr }, .{ | |
| 1504 | 1508 | .stdout = child.stdout.?, |
| 1505 | 1509 | .stderr = child.stderr.?, |
| 1506 | 1510 | }); |
| ... | ... | @@ -1524,11 +1528,6 @@ fn evalZigTest( |
| 1524 | 1528 | break :failed false; |
| 1525 | 1529 | }; |
| 1526 | 1530 | |
| 1527 | const Header = std.zig.Server.Message.Header; | |
| 1528 | ||
| 1529 | const stdout = poller.fifo(.stdout); | |
| 1530 | const stderr = poller.fifo(.stderr); | |
| 1531 | ||
| 1532 | 1531 | var fail_count: u32 = 0; |
| 1533 | 1532 | var skip_count: u32 = 0; |
| 1534 | 1533 | var leak_count: u32 = 0; |
| ... | ... | @@ -1541,16 +1540,14 @@ fn evalZigTest( |
| 1541 | 1540 | var sub_prog_node: ?std.Progress.Node = null; |
| 1542 | 1541 | defer if (sub_prog_node) |n| n.end(); |
| 1543 | 1542 | |
| 1543 | const stdout = poller.reader(.stdout); | |
| 1544 | const stderr = poller.reader(.stderr); | |
| 1544 | 1545 | const any_write_failed = first_write_failed or poll: while (true) { |
| 1545 | while (stdout.readableLength() < @sizeOf(Header)) { | |
| 1546 | if (!(try poller.poll())) break :poll false; | |
| 1547 | } | |
| 1548 | const header = stdout.reader().readStruct(Header) catch unreachable; | |
| 1549 | while (stdout.readableLength() < header.bytes_len) { | |
| 1550 | if (!(try poller.poll())) break :poll false; | |
| 1551 | } | |
| 1552 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 1553 | ||
| 1546 | const Header = std.zig.Server.Message.Header; | |
| 1547 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll false; | |
| 1548 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 1549 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll false; | |
| 1550 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 1554 | 1551 | switch (header.tag) { |
| 1555 | 1552 | .zig_version => { |
| 1556 | 1553 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { |
| ... | ... | @@ -1607,9 +1604,9 @@ fn evalZigTest( |
| 1607 | 1604 | |
| 1608 | 1605 | if (tr_hdr.flags.fail or tr_hdr.flags.leak or tr_hdr.flags.log_err_count > 0) { |
| 1609 | 1606 | const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0); |
| 1610 | const orig_msg = stderr.readableSlice(0); | |
| 1611 | defer stderr.discard(orig_msg.len); | |
| 1612 | const msg = std.mem.trim(u8, orig_msg, "\n"); | |
| 1607 | const stderr_contents = stderr.buffered(); | |
| 1608 | stderr.toss(stderr_contents.len); | |
| 1609 | const msg = std.mem.trim(u8, stderr_contents, "\n"); | |
| 1613 | 1610 | const label = if (tr_hdr.flags.fail) |
| 1614 | 1611 | "failed" |
| 1615 | 1612 | else if (tr_hdr.flags.leak) |
| ... | ... | @@ -1660,8 +1657,6 @@ fn evalZigTest( |
| 1660 | 1657 | }, |
| 1661 | 1658 | else => {}, // ignore other messages |
| 1662 | 1659 | } |
| 1663 | ||
| 1664 | stdout.discard(body.len); | |
| 1665 | 1660 | }; |
| 1666 | 1661 | |
| 1667 | 1662 | if (any_write_failed) { |
| ... | ... | @@ -1670,9 +1665,9 @@ fn evalZigTest( |
| 1670 | 1665 | while (try poller.poll()) {} |
| 1671 | 1666 | } |
| 1672 | 1667 | |
| 1673 | if (stderr.readableLength() > 0) { | |
| 1674 | const msg = std.mem.trim(u8, try stderr.toOwnedSlice(), "\n"); | |
| 1675 | if (msg.len > 0) run.step.result_stderr = msg; | |
| 1668 | const stderr_contents = std.mem.trim(u8, stderr.buffered(), "\n"); | |
| 1669 | if (stderr_contents.len > 0) { | |
| 1670 | run.step.result_stderr = try arena.dupe(u8, stderr_contents); | |
| 1676 | 1671 | } |
| 1677 | 1672 | |
| 1678 | 1673 | // Send EOF to stdin. |
| ... | ... | @@ -1795,28 +1790,43 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult { |
| 1795 | 1790 | var stdout_bytes: ?[]const u8 = null; |
| 1796 | 1791 | var stderr_bytes: ?[]const u8 = null; |
| 1797 | 1792 | |
| 1793 | run.stdio_limit = run.stdio_limit.min(.limited(run.max_stdio_size)); | |
| 1798 | 1794 | if (child.stdout) |stdout| { |
| 1799 | 1795 | if (child.stderr) |stderr| { |
| 1800 | var poller = std.io.poll(arena, enum { stdout, stderr }, .{ | |
| 1796 | var poller = std.Io.poll(arena, enum { stdout, stderr }, .{ | |
| 1801 | 1797 | .stdout = stdout, |
| 1802 | 1798 | .stderr = stderr, |
| 1803 | 1799 | }); |
| 1804 | 1800 | defer poller.deinit(); |
| 1805 | 1801 | |
| 1806 | 1802 | while (try poller.poll()) { |
| 1807 | if (poller.fifo(.stdout).count > run.max_stdio_size) | |
| 1808 | return error.StdoutStreamTooLong; | |
| 1809 | if (poller.fifo(.stderr).count > run.max_stdio_size) | |
| 1810 | return error.StderrStreamTooLong; | |
| 1803 | if (run.stdio_limit.toInt()) |limit| { | |
| 1804 | if (poller.reader(.stderr).buffered().len > limit) | |
| 1805 | return error.StdoutStreamTooLong; | |
| 1806 | if (poller.reader(.stderr).buffered().len > limit) | |
| 1807 | return error.StderrStreamTooLong; | |
| 1808 | } | |
| 1811 | 1809 | } |
| 1812 | 1810 | |
| 1813 | stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); | |
| 1814 | stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); | |
| 1811 | stdout_bytes = try poller.toOwnedSlice(.stdout); | |
| 1812 | stderr_bytes = try poller.toOwnedSlice(.stderr); | |
| 1815 | 1813 | } else { |
| 1816 | stdout_bytes = try stdout.deprecatedReader().readAllAlloc(arena, run.max_stdio_size); | |
| 1814 | var small_buffer: [1]u8 = undefined; | |
| 1815 | var stdout_reader = stdout.readerStreaming(&small_buffer); | |
| 1816 | stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | |
| 1817 | error.OutOfMemory => return error.OutOfMemory, | |
| 1818 | error.ReadFailed => return stdout_reader.err.?, | |
| 1819 | error.StreamTooLong => return error.StdoutStreamTooLong, | |
| 1820 | }; | |
| 1817 | 1821 | } |
| 1818 | 1822 | } else if (child.stderr) |stderr| { |
| 1819 | stderr_bytes = try stderr.deprecatedReader().readAllAlloc(arena, run.max_stdio_size); | |
| 1823 | var small_buffer: [1]u8 = undefined; | |
| 1824 | var stderr_reader = stderr.readerStreaming(&small_buffer); | |
| 1825 | stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | |
| 1826 | error.OutOfMemory => return error.OutOfMemory, | |
| 1827 | error.ReadFailed => return stderr_reader.err.?, | |
| 1828 | error.StreamTooLong => return error.StderrStreamTooLong, | |
| 1829 | }; | |
| 1820 | 1830 | } |
| 1821 | 1831 | |
| 1822 | 1832 | if (stderr_bytes) |bytes| if (bytes.len > 0) { |
lib/std/Io.zig+228-176| ... | ... | @@ -1,16 +1,11 @@ |
| 1 | const std = @import("std.zig"); | |
| 2 | 1 | const builtin = @import("builtin"); |
| 3 | const root = @import("root"); | |
| 4 | const c = std.c; | |
| 5 | 2 | const is_windows = builtin.os.tag == .windows; |
| 3 | ||
| 4 | const std = @import("std.zig"); | |
| 6 | 5 | const windows = std.os.windows; |
| 7 | 6 | const posix = std.posix; |
| 8 | 7 | const math = std.math; |
| 9 | 8 | const assert = std.debug.assert; |
| 10 | const fs = std.fs; | |
| 11 | const mem = std.mem; | |
| 12 | const meta = std.meta; | |
| 13 | const File = std.fs.File; | |
| 14 | 9 | const Allocator = std.mem.Allocator; |
| 15 | 10 | const Alignment = std.mem.Alignment; |
| 16 | 11 | |
| ... | ... | @@ -493,54 +488,51 @@ test null_writer { |
| 493 | 488 | } |
| 494 | 489 | |
| 495 | 490 | pub fn poll( |
| 496 | allocator: Allocator, | |
| 491 | gpa: Allocator, | |
| 497 | 492 | comptime StreamEnum: type, |
| 498 | 493 | files: PollFiles(StreamEnum), |
| 499 | 494 | ) Poller(StreamEnum) { |
| 500 | 495 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; |
| 501 | var result: Poller(StreamEnum) = undefined; | |
| 502 | ||
| 503 | if (is_windows) result.windows = .{ | |
| 504 | .first_read_done = false, | |
| 505 | .overlapped = [1]windows.OVERLAPPED{ | |
| 506 | mem.zeroes(windows.OVERLAPPED), | |
| 507 | } ** enum_fields.len, | |
| 508 | .small_bufs = undefined, | |
| 509 | .active = .{ | |
| 510 | .count = 0, | |
| 511 | .handles_buf = undefined, | |
| 512 | .stream_map = undefined, | |
| 513 | }, | |
| 496 | var result: Poller(StreamEnum) = .{ | |
| 497 | .gpa = gpa, | |
| 498 | .readers = @splat(.failing), | |
| 499 | .poll_fds = undefined, | |
| 500 | .windows = if (is_windows) .{ | |
| 501 | .first_read_done = false, | |
| 502 | .overlapped = [1]windows.OVERLAPPED{ | |
| 503 | std.mem.zeroes(windows.OVERLAPPED), | |
| 504 | } ** enum_fields.len, | |
| 505 | .small_bufs = undefined, | |
| 506 | .active = .{ | |
| 507 | .count = 0, | |
| 508 | .handles_buf = undefined, | |
| 509 | .stream_map = undefined, | |
| 510 | }, | |
| 511 | } else {}, | |
| 514 | 512 | }; |
| 515 | 513 | |
| 516 | inline for (0..enum_fields.len) |i| { | |
| 517 | result.fifos[i] = .{ | |
| 518 | .allocator = allocator, | |
| 519 | .buf = &.{}, | |
| 520 | .head = 0, | |
| 521 | .count = 0, | |
| 522 | }; | |
| 514 | inline for (enum_fields, 0..) |field, i| { | |
| 523 | 515 | if (is_windows) { |
| 524 | result.windows.active.handles_buf[i] = @field(files, enum_fields[i].name).handle; | |
| 516 | result.windows.active.handles_buf[i] = @field(files, field.name).handle; | |
| 525 | 517 | } else { |
| 526 | 518 | result.poll_fds[i] = .{ |
| 527 | .fd = @field(files, enum_fields[i].name).handle, | |
| 519 | .fd = @field(files, field.name).handle, | |
| 528 | 520 | .events = posix.POLL.IN, |
| 529 | 521 | .revents = undefined, |
| 530 | 522 | }; |
| 531 | 523 | } |
| 532 | 524 | } |
| 525 | ||
| 533 | 526 | return result; |
| 534 | 527 | } |
| 535 | 528 | |
| 536 | pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic); | |
| 537 | ||
| 538 | 529 | pub fn Poller(comptime StreamEnum: type) type { |
| 539 | 530 | return struct { |
| 540 | 531 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; |
| 541 | 532 | const PollFd = if (is_windows) void else posix.pollfd; |
| 542 | 533 | |
| 543 | fifos: [enum_fields.len]PollFifo, | |
| 534 | gpa: Allocator, | |
| 535 | readers: [enum_fields.len]Reader, | |
| 544 | 536 | poll_fds: [enum_fields.len]PollFd, |
| 545 | 537 | windows: if (is_windows) struct { |
| 546 | 538 | first_read_done: bool, |
| ... | ... | @@ -552,7 +544,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 552 | 544 | stream_map: [enum_fields.len]StreamEnum, |
| 553 | 545 | |
| 554 | 546 | pub fn removeAt(self: *@This(), index: u32) void { |
| 555 | std.debug.assert(index < self.count); | |
| 547 | assert(index < self.count); | |
| 556 | 548 | for (index + 1..self.count) |i| { |
| 557 | 549 | self.handles_buf[i - 1] = self.handles_buf[i]; |
| 558 | 550 | self.stream_map[i - 1] = self.stream_map[i]; |
| ... | ... | @@ -565,13 +557,14 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 565 | 557 | const Self = @This(); |
| 566 | 558 | |
| 567 | 559 | pub fn deinit(self: *Self) void { |
| 560 | const gpa = self.gpa; | |
| 568 | 561 | if (is_windows) { |
| 569 | 562 | // cancel any pending IO to prevent clobbering OVERLAPPED value |
| 570 | 563 | for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| { |
| 571 | 564 | _ = windows.kernel32.CancelIo(h); |
| 572 | 565 | } |
| 573 | 566 | } |
| 574 | inline for (&self.fifos) |*q| q.deinit(); | |
| 567 | inline for (&self.readers) |*r| gpa.free(r.buffer); | |
| 575 | 568 | self.* = undefined; |
| 576 | 569 | } |
| 577 | 570 | |
| ... | ... | @@ -591,21 +584,40 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 591 | 584 | } |
| 592 | 585 | } |
| 593 | 586 | |
| 594 | pub inline fn fifo(self: *Self, comptime which: StreamEnum) *PollFifo { | |
| 595 | return &self.fifos[@intFromEnum(which)]; | |
| 587 | pub fn reader(self: *Self, which: StreamEnum) *Reader { | |
| 588 | return &self.readers[@intFromEnum(which)]; | |
| 589 | } | |
| 590 | ||
| 591 | pub fn toOwnedSlice(self: *Self, which: StreamEnum) error{OutOfMemory}![]u8 { | |
| 592 | const gpa = self.gpa; | |
| 593 | const r = reader(self, which); | |
| 594 | if (r.seek == 0) { | |
| 595 | const new = try gpa.realloc(r.buffer, r.end); | |
| 596 | r.buffer = &.{}; | |
| 597 | r.end = 0; | |
| 598 | return new; | |
| 599 | } | |
| 600 | const new = try gpa.dupe(u8, r.buffered()); | |
| 601 | gpa.free(r.buffer); | |
| 602 | r.buffer = &.{}; | |
| 603 | r.seek = 0; | |
| 604 | r.end = 0; | |
| 605 | return new; | |
| 596 | 606 | } |
| 597 | 607 | |
| 598 | 608 | fn pollWindows(self: *Self, nanoseconds: ?u64) !bool { |
| 599 | 609 | const bump_amt = 512; |
| 610 | const gpa = self.gpa; | |
| 600 | 611 | |
| 601 | 612 | if (!self.windows.first_read_done) { |
| 602 | 613 | var already_read_data = false; |
| 603 | 614 | for (0..enum_fields.len) |i| { |
| 604 | 615 | const handle = self.windows.active.handles_buf[i]; |
| 605 | 616 | switch (try windowsAsyncReadToFifoAndQueueSmallRead( |
| 617 | gpa, | |
| 606 | 618 | handle, |
| 607 | 619 | &self.windows.overlapped[i], |
| 608 | &self.fifos[i], | |
| 620 | &self.readers[i], | |
| 609 | 621 | &self.windows.small_bufs[i], |
| 610 | 622 | bump_amt, |
| 611 | 623 | )) { |
| ... | ... | @@ -652,7 +664,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 652 | 664 | const handle = self.windows.active.handles_buf[active_idx]; |
| 653 | 665 | |
| 654 | 666 | const overlapped = &self.windows.overlapped[stream_idx]; |
| 655 | const stream_fifo = &self.fifos[stream_idx]; | |
| 667 | const stream_reader = &self.readers[stream_idx]; | |
| 656 | 668 | const small_buf = &self.windows.small_bufs[stream_idx]; |
| 657 | 669 | |
| 658 | 670 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { |
| ... | ... | @@ -663,12 +675,16 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 663 | 675 | }, |
| 664 | 676 | .aborted => unreachable, |
| 665 | 677 | }; |
| 666 | try stream_fifo.write(small_buf[0..num_bytes_read]); | |
| 678 | const buf = small_buf[0..num_bytes_read]; | |
| 679 | const dest = try writableSliceGreedyAlloc(stream_reader, gpa, buf.len); | |
| 680 | @memcpy(dest[0..buf.len], buf); | |
| 681 | advanceBufferEnd(stream_reader, buf.len); | |
| 667 | 682 | |
| 668 | 683 | switch (try windowsAsyncReadToFifoAndQueueSmallRead( |
| 684 | gpa, | |
| 669 | 685 | handle, |
| 670 | 686 | overlapped, |
| 671 | stream_fifo, | |
| 687 | stream_reader, | |
| 672 | 688 | small_buf, |
| 673 | 689 | bump_amt, |
| 674 | 690 | )) { |
| ... | ... | @@ -683,6 +699,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 683 | 699 | } |
| 684 | 700 | |
| 685 | 701 | fn pollPosix(self: *Self, nanoseconds: ?u64) !bool { |
| 702 | const gpa = self.gpa; | |
| 686 | 703 | // We ask for ensureUnusedCapacity with this much extra space. This |
| 687 | 704 | // has more of an effect on small reads because once the reads |
| 688 | 705 | // start to get larger the amount of space an ArrayList will |
| ... | ... | @@ -702,18 +719,18 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 702 | 719 | } |
| 703 | 720 | |
| 704 | 721 | var keep_polling = false; |
| 705 | inline for (&self.poll_fds, &self.fifos) |*poll_fd, *q| { | |
| 722 | for (&self.poll_fds, &self.readers) |*poll_fd, *r| { | |
| 706 | 723 | // Try reading whatever is available before checking the error |
| 707 | 724 | // conditions. |
| 708 | 725 | // It's still possible to read after a POLL.HUP is received, |
| 709 | 726 | // always check if there's some data waiting to be read first. |
| 710 | 727 | if (poll_fd.revents & posix.POLL.IN != 0) { |
| 711 | const buf = try q.writableWithSize(bump_amt); | |
| 728 | const buf = try writableSliceGreedyAlloc(r, gpa, bump_amt); | |
| 712 | 729 | const amt = posix.read(poll_fd.fd, buf) catch |err| switch (err) { |
| 713 | 730 | error.BrokenPipe => 0, // Handle the same as EOF. |
| 714 | 731 | else => |e| return e, |
| 715 | 732 | }; |
| 716 | q.update(amt); | |
| 733 | advanceBufferEnd(r, amt); | |
| 717 | 734 | if (amt == 0) { |
| 718 | 735 | // Remove the fd when the EOF condition is met. |
| 719 | 736 | poll_fd.fd = -1; |
| ... | ... | @@ -729,146 +746,181 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 729 | 746 | } |
| 730 | 747 | return keep_polling; |
| 731 | 748 | } |
| 732 | }; | |
| 733 | } | |
| 734 | 749 | |
| 735 | /// The `ReadFile` docuementation states that `lpNumberOfBytesRead` does not have a meaningful | |
| 736 | /// result when using overlapped I/O, but also that it cannot be `null` on Windows 7. For | |
| 737 | /// compatibility, we point it to this dummy variables, which we never otherwise access. | |
| 738 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | |
| 739 | var win_dummy_bytes_read: u32 = undefined; | |
| 740 | ||
| 741 | /// Read as much data as possible from `handle` with `overlapped`, and write it to the FIFO. Before | |
| 742 | /// returning, queue a read into `small_buf` so that `WaitForMultipleObjects` returns when more data | |
| 743 | /// is available. `handle` must have no pending asynchronous operation. | |
| 744 | fn windowsAsyncReadToFifoAndQueueSmallRead( | |
| 745 | handle: windows.HANDLE, | |
| 746 | overlapped: *windows.OVERLAPPED, | |
| 747 | fifo: *PollFifo, | |
| 748 | small_buf: *[128]u8, | |
| 749 | bump_amt: usize, | |
| 750 | ) !enum { empty, populated, closed_populated, closed } { | |
| 751 | var read_any_data = false; | |
| 752 | while (true) { | |
| 753 | const fifo_read_pending = while (true) { | |
| 754 | const buf = try fifo.writableWithSize(bump_amt); | |
| 755 | const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32); | |
| 756 | ||
| 757 | if (0 == windows.kernel32.ReadFile( | |
| 758 | handle, | |
| 759 | buf.ptr, | |
| 760 | buf_len, | |
| 761 | &win_dummy_bytes_read, | |
| 762 | overlapped, | |
| 763 | )) switch (windows.GetLastError()) { | |
| 764 | .IO_PENDING => break true, | |
| 765 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, | |
| 766 | else => |err| return windows.unexpectedError(err), | |
| 767 | }; | |
| 750 | /// Returns a slice into the unused capacity of `buffer` with at least | |
| 751 | /// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary. | |
| 752 | /// | |
| 753 | /// After calling this function, typically the caller will follow up with a | |
| 754 | /// call to `advanceBufferEnd` to report the actual number of bytes buffered. | |
| 755 | fn writableSliceGreedyAlloc(r: *Reader, allocator: Allocator, min_len: usize) Allocator.Error![]u8 { | |
| 756 | { | |
| 757 | const unused = r.buffer[r.end..]; | |
| 758 | if (unused.len >= min_len) return unused; | |
| 759 | } | |
| 760 | if (r.seek > 0) r.rebase(); | |
| 761 | { | |
| 762 | var list: std.ArrayListUnmanaged(u8) = .{ | |
| 763 | .items = r.buffer[0..r.end], | |
| 764 | .capacity = r.buffer.len, | |
| 765 | }; | |
| 766 | defer r.buffer = list.allocatedSlice(); | |
| 767 | try list.ensureUnusedCapacity(allocator, min_len); | |
| 768 | } | |
| 769 | const unused = r.buffer[r.end..]; | |
| 770 | assert(unused.len >= min_len); | |
| 771 | return unused; | |
| 772 | } | |
| 773 | ||
| 774 | /// After writing directly into the unused capacity of `buffer`, this function | |
| 775 | /// updates `end` so that users of `Reader` can receive the data. | |
| 776 | fn advanceBufferEnd(r: *Reader, n: usize) void { | |
| 777 | assert(n <= r.buffer.len - r.end); | |
| 778 | r.end += n; | |
| 779 | } | |
| 780 | ||
| 781 | /// The `ReadFile` docuementation states that `lpNumberOfBytesRead` does not have a meaningful | |
| 782 | /// result when using overlapped I/O, but also that it cannot be `null` on Windows 7. For | |
| 783 | /// compatibility, we point it to this dummy variables, which we never otherwise access. | |
| 784 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | |
| 785 | var win_dummy_bytes_read: u32 = undefined; | |
| 786 | ||
| 787 | /// Read as much data as possible from `handle` with `overlapped`, and write it to the FIFO. Before | |
| 788 | /// returning, queue a read into `small_buf` so that `WaitForMultipleObjects` returns when more data | |
| 789 | /// is available. `handle` must have no pending asynchronous operation. | |
| 790 | fn windowsAsyncReadToFifoAndQueueSmallRead( | |
| 791 | gpa: Allocator, | |
| 792 | handle: windows.HANDLE, | |
| 793 | overlapped: *windows.OVERLAPPED, | |
| 794 | r: *Reader, | |
| 795 | small_buf: *[128]u8, | |
| 796 | bump_amt: usize, | |
| 797 | ) !enum { empty, populated, closed_populated, closed } { | |
| 798 | var read_any_data = false; | |
| 799 | while (true) { | |
| 800 | const fifo_read_pending = while (true) { | |
| 801 | const buf = try writableSliceGreedyAlloc(r, gpa, bump_amt); | |
| 802 | const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32); | |
| 768 | 803 | |
| 769 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | |
| 770 | .success => |n| n, | |
| 771 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 772 | .aborted => unreachable, | |
| 773 | }; | |
| 804 | if (0 == windows.kernel32.ReadFile( | |
| 805 | handle, | |
| 806 | buf.ptr, | |
| 807 | buf_len, | |
| 808 | &win_dummy_bytes_read, | |
| 809 | overlapped, | |
| 810 | )) switch (windows.GetLastError()) { | |
| 811 | .IO_PENDING => break true, | |
| 812 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, | |
| 813 | else => |err| return windows.unexpectedError(err), | |
| 814 | }; | |
| 774 | 815 | |
| 775 | read_any_data = true; | |
| 776 | fifo.update(num_bytes_read); | |
| 816 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | |
| 817 | .success => |n| n, | |
| 818 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 819 | .aborted => unreachable, | |
| 820 | }; | |
| 777 | 821 | |
| 778 | if (num_bytes_read == buf_len) { | |
| 779 | // We filled the buffer, so there's probably more data available. | |
| 780 | continue; | |
| 781 | } else { | |
| 782 | // We didn't fill the buffer, so assume we're out of data. | |
| 783 | // There is no pending read. | |
| 784 | break false; | |
| 785 | } | |
| 786 | }; | |
| 822 | read_any_data = true; | |
| 823 | advanceBufferEnd(r, num_bytes_read); | |
| 787 | 824 | |
| 788 | if (fifo_read_pending) cancel_read: { | |
| 789 | // Cancel the pending read into the FIFO. | |
| 790 | _ = windows.kernel32.CancelIo(handle); | |
| 825 | if (num_bytes_read == buf_len) { | |
| 826 | // We filled the buffer, so there's probably more data available. | |
| 827 | continue; | |
| 828 | } else { | |
| 829 | // We didn't fill the buffer, so assume we're out of data. | |
| 830 | // There is no pending read. | |
| 831 | break false; | |
| 832 | } | |
| 833 | }; | |
| 791 | 834 | |
| 792 | // We have to wait for the handle to be signalled, i.e. for the cancellation to complete. | |
| 793 | switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) { | |
| 794 | windows.WAIT_OBJECT_0 => {}, | |
| 795 | windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()), | |
| 796 | else => unreachable, | |
| 797 | } | |
| 835 | if (fifo_read_pending) cancel_read: { | |
| 836 | // Cancel the pending read into the FIFO. | |
| 837 | _ = windows.kernel32.CancelIo(handle); | |
| 798 | 838 | |
| 799 | // If it completed before we canceled, make sure to tell the FIFO! | |
| 800 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, true)) { | |
| 801 | .success => |n| n, | |
| 802 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 803 | .aborted => break :cancel_read, | |
| 804 | }; | |
| 805 | read_any_data = true; | |
| 806 | fifo.update(num_bytes_read); | |
| 807 | } | |
| 808 | ||
| 809 | // Try to queue the 1-byte read. | |
| 810 | if (0 == windows.kernel32.ReadFile( | |
| 811 | handle, | |
| 812 | small_buf, | |
| 813 | small_buf.len, | |
| 814 | &win_dummy_bytes_read, | |
| 815 | overlapped, | |
| 816 | )) switch (windows.GetLastError()) { | |
| 817 | .IO_PENDING => { | |
| 818 | // 1-byte read pending as intended | |
| 819 | return if (read_any_data) .populated else .empty; | |
| 820 | }, | |
| 821 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, | |
| 822 | else => |err| return windows.unexpectedError(err), | |
| 823 | }; | |
| 839 | // We have to wait for the handle to be signalled, i.e. for the cancellation to complete. | |
| 840 | switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) { | |
| 841 | windows.WAIT_OBJECT_0 => {}, | |
| 842 | windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()), | |
| 843 | else => unreachable, | |
| 844 | } | |
| 824 | 845 | |
| 825 | // We got data back this time. Write it to the FIFO and run the main loop again. | |
| 826 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | |
| 827 | .success => |n| n, | |
| 828 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 829 | .aborted => unreachable, | |
| 830 | }; | |
| 831 | try fifo.write(small_buf[0..num_bytes_read]); | |
| 832 | read_any_data = true; | |
| 833 | } | |
| 834 | } | |
| 846 | // If it completed before we canceled, make sure to tell the FIFO! | |
| 847 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, true)) { | |
| 848 | .success => |n| n, | |
| 849 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 850 | .aborted => break :cancel_read, | |
| 851 | }; | |
| 852 | read_any_data = true; | |
| 853 | advanceBufferEnd(r, num_bytes_read); | |
| 854 | } | |
| 835 | 855 | |
| 836 | /// Simple wrapper around `GetOverlappedResult` to determine the result of a `ReadFile` operation. | |
| 837 | /// If `!allow_aborted`, then `aborted` is never returned (`OPERATION_ABORTED` is considered unexpected). | |
| 838 | /// | |
| 839 | /// The `ReadFile` documentation states that the number of bytes read by an overlapped `ReadFile` must be determined using `GetOverlappedResult`, even if the | |
| 840 | /// operation immediately returns data: | |
| 841 | /// "Use NULL for [lpNumberOfBytesRead] if this is an asynchronous operation to avoid potentially | |
| 842 | /// erroneous results." | |
| 843 | /// "If `hFile` was opened with `FILE_FLAG_OVERLAPPED`, the following conditions are in effect: [...] | |
| 844 | /// The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to | |
| 845 | /// get the actual number of bytes read." | |
| 846 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | |
| 847 | fn windowsGetReadResult( | |
| 848 | handle: windows.HANDLE, | |
| 849 | overlapped: *windows.OVERLAPPED, | |
| 850 | allow_aborted: bool, | |
| 851 | ) !union(enum) { | |
| 852 | success: u32, | |
| 853 | closed, | |
| 854 | aborted, | |
| 855 | } { | |
| 856 | var num_bytes_read: u32 = undefined; | |
| 857 | if (0 == windows.kernel32.GetOverlappedResult( | |
| 858 | handle, | |
| 859 | overlapped, | |
| 860 | &num_bytes_read, | |
| 861 | 0, | |
| 862 | )) switch (windows.GetLastError()) { | |
| 863 | .BROKEN_PIPE => return .closed, | |
| 864 | .OPERATION_ABORTED => |err| if (allow_aborted) { | |
| 865 | return .aborted; | |
| 866 | } else { | |
| 867 | return windows.unexpectedError(err); | |
| 868 | }, | |
| 869 | else => |err| return windows.unexpectedError(err), | |
| 856 | // Try to queue the 1-byte read. | |
| 857 | if (0 == windows.kernel32.ReadFile( | |
| 858 | handle, | |
| 859 | small_buf, | |
| 860 | small_buf.len, | |
| 861 | &win_dummy_bytes_read, | |
| 862 | overlapped, | |
| 863 | )) switch (windows.GetLastError()) { | |
| 864 | .IO_PENDING => { | |
| 865 | // 1-byte read pending as intended | |
| 866 | return if (read_any_data) .populated else .empty; | |
| 867 | }, | |
| 868 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, | |
| 869 | else => |err| return windows.unexpectedError(err), | |
| 870 | }; | |
| 871 | ||
| 872 | // We got data back this time. Write it to the FIFO and run the main loop again. | |
| 873 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | |
| 874 | .success => |n| n, | |
| 875 | .closed => return if (read_any_data) .closed_populated else .closed, | |
| 876 | .aborted => unreachable, | |
| 877 | }; | |
| 878 | const buf = small_buf[0..num_bytes_read]; | |
| 879 | const dest = try writableSliceGreedyAlloc(r, gpa, buf.len); | |
| 880 | @memcpy(dest[0..buf.len], buf); | |
| 881 | advanceBufferEnd(r, buf.len); | |
| 882 | read_any_data = true; | |
| 883 | } | |
| 884 | } | |
| 885 | ||
| 886 | /// Simple wrapper around `GetOverlappedResult` to determine the result of a `ReadFile` operation. | |
| 887 | /// If `!allow_aborted`, then `aborted` is never returned (`OPERATION_ABORTED` is considered unexpected). | |
| 888 | /// | |
| 889 | /// The `ReadFile` documentation states that the number of bytes read by an overlapped `ReadFile` must be determined using `GetOverlappedResult`, even if the | |
| 890 | /// operation immediately returns data: | |
| 891 | /// "Use NULL for [lpNumberOfBytesRead] if this is an asynchronous operation to avoid potentially | |
| 892 | /// erroneous results." | |
| 893 | /// "If `hFile` was opened with `FILE_FLAG_OVERLAPPED`, the following conditions are in effect: [...] | |
| 894 | /// The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to | |
| 895 | /// get the actual number of bytes read." | |
| 896 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | |
| 897 | fn windowsGetReadResult( | |
| 898 | handle: windows.HANDLE, | |
| 899 | overlapped: *windows.OVERLAPPED, | |
| 900 | allow_aborted: bool, | |
| 901 | ) !union(enum) { | |
| 902 | success: u32, | |
| 903 | closed, | |
| 904 | aborted, | |
| 905 | } { | |
| 906 | var num_bytes_read: u32 = undefined; | |
| 907 | if (0 == windows.kernel32.GetOverlappedResult( | |
| 908 | handle, | |
| 909 | overlapped, | |
| 910 | &num_bytes_read, | |
| 911 | 0, | |
| 912 | )) switch (windows.GetLastError()) { | |
| 913 | .BROKEN_PIPE => return .closed, | |
| 914 | .OPERATION_ABORTED => |err| if (allow_aborted) { | |
| 915 | return .aborted; | |
| 916 | } else { | |
| 917 | return windows.unexpectedError(err); | |
| 918 | }, | |
| 919 | else => |err| return windows.unexpectedError(err), | |
| 920 | }; | |
| 921 | return .{ .success = num_bytes_read }; | |
| 922 | } | |
| 870 | 923 | }; |
| 871 | return .{ .success = num_bytes_read }; | |
| 872 | 924 | } |
| 873 | 925 | |
| 874 | 926 | /// Given an enum, returns a struct with fields of that enum, each field |
| ... | ... | @@ -879,10 +931,10 @@ pub fn PollFiles(comptime StreamEnum: type) type { |
| 879 | 931 | for (&struct_fields, enum_fields) |*struct_field, enum_field| { |
| 880 | 932 | struct_field.* = .{ |
| 881 | 933 | .name = enum_field.name, |
| 882 | .type = fs.File, | |
| 934 | .type = std.fs.File, | |
| 883 | 935 | .default_value_ptr = null, |
| 884 | 936 | .is_comptime = false, |
| 885 | .alignment = @alignOf(fs.File), | |
| 937 | .alignment = @alignOf(std.fs.File), | |
| 886 | 938 | }; |
| 887 | 939 | } |
| 888 | 940 | return @Type(.{ .@"struct" = .{ |
lib/std/Io/Reader.zig-31| ... | ... | @@ -1241,37 +1241,6 @@ pub fn fillAlloc(r: *Reader, allocator: Allocator, n: usize) FillAllocError!void |
| 1241 | 1241 | return fill(r, n); |
| 1242 | 1242 | } |
| 1243 | 1243 | |
| 1244 | /// Returns a slice into the unused capacity of `buffer` with at least | |
| 1245 | /// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary. | |
| 1246 | /// | |
| 1247 | /// After calling this function, typically the caller will follow up with a | |
| 1248 | /// call to `advanceBufferEnd` to report the actual number of bytes buffered. | |
| 1249 | pub fn writableSliceGreedyAlloc(r: *Reader, allocator: Allocator, min_len: usize) Allocator.Error![]u8 { | |
| 1250 | { | |
| 1251 | const unused = r.buffer[r.end..]; | |
| 1252 | if (unused.len >= min_len) return unused; | |
| 1253 | } | |
| 1254 | if (r.seek > 0) rebase(r); | |
| 1255 | { | |
| 1256 | var list: ArrayList(u8) = .{ | |
| 1257 | .items = r.buffer[0..r.end], | |
| 1258 | .capacity = r.buffer.len, | |
| 1259 | }; | |
| 1260 | defer r.buffer = list.allocatedSlice(); | |
| 1261 | try list.ensureUnusedCapacity(allocator, min_len); | |
| 1262 | } | |
| 1263 | const unused = r.buffer[r.end..]; | |
| 1264 | assert(unused.len >= min_len); | |
| 1265 | return unused; | |
| 1266 | } | |
| 1267 | ||
| 1268 | /// After writing directly into the unused capacity of `buffer`, this function | |
| 1269 | /// updates `end` so that users of `Reader` can receive the data. | |
| 1270 | pub fn advanceBufferEnd(r: *Reader, n: usize) void { | |
| 1271 | assert(n <= r.buffer.len - r.end); | |
| 1272 | r.end += n; | |
| 1273 | } | |
| 1274 | ||
| 1275 | 1244 | fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result { |
| 1276 | 1245 | const result_info = @typeInfo(Result).int; |
| 1277 | 1246 | comptime assert(result_info.bits % 7 == 0); |
lib/std/process/Child.zig+46-35| ... | ... | @@ -14,6 +14,7 @@ const assert = std.debug.assert; |
| 14 | 14 | const native_os = builtin.os.tag; |
| 15 | 15 | const Allocator = std.mem.Allocator; |
| 16 | 16 | const ChildProcess = @This(); |
| 17 | const ArrayList = std.ArrayListUnmanaged; | |
| 17 | 18 | |
| 18 | 19 | pub const Id = switch (native_os) { |
| 19 | 20 | .windows => windows.HANDLE, |
| ... | ... | @@ -348,19 +349,6 @@ pub const RunResult = struct { |
| 348 | 349 | stderr: []u8, |
| 349 | 350 | }; |
| 350 | 351 | |
| 351 | fn writeFifoDataToArrayList(allocator: Allocator, list: *std.ArrayListUnmanaged(u8), fifo: *std.io.PollFifo) !void { | |
| 352 | if (fifo.head != 0) fifo.realign(); | |
| 353 | if (list.capacity == 0) { | |
| 354 | list.* = .{ | |
| 355 | .items = fifo.buf[0..fifo.count], | |
| 356 | .capacity = fifo.buf.len, | |
| 357 | }; | |
| 358 | fifo.* = std.io.PollFifo.init(fifo.allocator); | |
| 359 | } else { | |
| 360 | try list.appendSlice(allocator, fifo.buf[0..fifo.count]); | |
| 361 | } | |
| 362 | } | |
| 363 | ||
| 364 | 352 | /// Collect the output from the process's stdout and stderr. Will return once all output |
| 365 | 353 | /// has been collected. This does not mean that the process has ended. `wait` should still |
| 366 | 354 | /// be called to wait for and clean up the process. |
| ... | ... | @@ -370,28 +358,48 @@ pub fn collectOutput( |
| 370 | 358 | child: ChildProcess, |
| 371 | 359 | /// Used for `stdout` and `stderr`. |
| 372 | 360 | allocator: Allocator, |
| 373 | stdout: *std.ArrayListUnmanaged(u8), | |
| 374 | stderr: *std.ArrayListUnmanaged(u8), | |
| 361 | stdout: *ArrayList(u8), | |
| 362 | stderr: *ArrayList(u8), | |
| 375 | 363 | max_output_bytes: usize, |
| 376 | 364 | ) !void { |
| 377 | 365 | assert(child.stdout_behavior == .Pipe); |
| 378 | 366 | assert(child.stderr_behavior == .Pipe); |
| 379 | 367 | |
| 380 | var poller = std.io.poll(allocator, enum { stdout, stderr }, .{ | |
| 368 | var poller = std.Io.poll(allocator, enum { stdout, stderr }, .{ | |
| 381 | 369 | .stdout = child.stdout.?, |
| 382 | 370 | .stderr = child.stderr.?, |
| 383 | 371 | }); |
| 384 | 372 | defer poller.deinit(); |
| 385 | 373 | |
| 374 | const stdout_r = poller.reader(.stdout); | |
| 375 | stdout_r.buffer = stdout.allocatedSlice(); | |
| 376 | stdout_r.seek = 0; | |
| 377 | stdout_r.end = stdout.items.len; | |
| 378 | ||
| 379 | const stderr_r = poller.reader(.stderr); | |
| 380 | stderr_r.buffer = stderr.allocatedSlice(); | |
| 381 | stderr_r.seek = 0; | |
| 382 | stderr_r.end = stderr.items.len; | |
| 383 | ||
| 384 | defer { | |
| 385 | stdout.* = .{ | |
| 386 | .items = stdout_r.buffer[0..stdout_r.end], | |
| 387 | .capacity = stdout_r.buffer.len, | |
| 388 | }; | |
| 389 | stderr.* = .{ | |
| 390 | .items = stderr_r.buffer[0..stderr_r.end], | |
| 391 | .capacity = stderr_r.buffer.len, | |
| 392 | }; | |
| 393 | stdout_r.buffer = &.{}; | |
| 394 | stderr_r.buffer = &.{}; | |
| 395 | } | |
| 396 | ||
| 386 | 397 | while (try poller.poll()) { |
| 387 | if (poller.fifo(.stdout).count > max_output_bytes) | |
| 398 | if (stdout_r.bufferedLen() > max_output_bytes) | |
| 388 | 399 | return error.StdoutStreamTooLong; |
| 389 | if (poller.fifo(.stderr).count > max_output_bytes) | |
| 400 | if (stderr_r.bufferedLen() > max_output_bytes) | |
| 390 | 401 | return error.StderrStreamTooLong; |
| 391 | 402 | } |
| 392 | ||
| 393 | try writeFifoDataToArrayList(allocator, stdout, poller.fifo(.stdout)); | |
| 394 | try writeFifoDataToArrayList(allocator, stderr, poller.fifo(.stderr)); | |
| 395 | 403 | } |
| 396 | 404 | |
| 397 | 405 | pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{ |
| ... | ... | @@ -421,10 +429,10 @@ pub fn run(args: struct { |
| 421 | 429 | child.expand_arg0 = args.expand_arg0; |
| 422 | 430 | child.progress_node = args.progress_node; |
| 423 | 431 | |
| 424 | var stdout: std.ArrayListUnmanaged(u8) = .empty; | |
| 425 | errdefer stdout.deinit(args.allocator); | |
| 426 | var stderr: std.ArrayListUnmanaged(u8) = .empty; | |
| 427 | errdefer stderr.deinit(args.allocator); | |
| 432 | var stdout: ArrayList(u8) = .empty; | |
| 433 | defer stdout.deinit(args.allocator); | |
| 434 | var stderr: ArrayList(u8) = .empty; | |
| 435 | defer stderr.deinit(args.allocator); | |
| 428 | 436 | |
| 429 | 437 | try child.spawn(); |
| 430 | 438 | errdefer { |
| ... | ... | @@ -432,7 +440,7 @@ pub fn run(args: struct { |
| 432 | 440 | } |
| 433 | 441 | try child.collectOutput(args.allocator, &stdout, &stderr, args.max_output_bytes); |
| 434 | 442 | |
| 435 | return RunResult{ | |
| 443 | return .{ | |
| 436 | 444 | .stdout = try stdout.toOwnedSlice(args.allocator), |
| 437 | 445 | .stderr = try stderr.toOwnedSlice(args.allocator), |
| 438 | 446 | .term = try child.wait(), |
| ... | ... | @@ -878,12 +886,12 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void { |
| 878 | 886 | var cmd_line_cache = WindowsCommandLineCache.init(self.allocator, self.argv); |
| 879 | 887 | defer cmd_line_cache.deinit(); |
| 880 | 888 | |
| 881 | var app_buf: std.ArrayListUnmanaged(u16) = .empty; | |
| 889 | var app_buf: ArrayList(u16) = .empty; | |
| 882 | 890 | defer app_buf.deinit(self.allocator); |
| 883 | 891 | |
| 884 | 892 | try app_buf.appendSlice(self.allocator, app_name_w); |
| 885 | 893 | |
| 886 | var dir_buf: std.ArrayListUnmanaged(u16) = .empty; | |
| 894 | var dir_buf: ArrayList(u16) = .empty; | |
| 887 | 895 | defer dir_buf.deinit(self.allocator); |
| 888 | 896 | |
| 889 | 897 | if (cwd_path_w.len > 0) { |
| ... | ... | @@ -1003,13 +1011,16 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { |
| 1003 | 1011 | } |
| 1004 | 1012 | |
| 1005 | 1013 | fn writeIntFd(fd: i32, value: ErrInt) !void { |
| 1006 | const file: File = .{ .handle = fd }; | |
| 1007 | file.deprecatedWriter().writeInt(u64, @intCast(value), .little) catch return error.SystemResources; | |
| 1014 | var buffer: [8]u8 = undefined; | |
| 1015 | var fw: std.fs.File.Writer = .initMode(.{ .handle = fd }, &buffer, .streaming); | |
| 1016 | fw.interface.writeInt(u64, value, .little) catch unreachable; | |
| 1017 | fw.interface.flush() catch return error.SystemResources; | |
| 1008 | 1018 | } |
| 1009 | 1019 | |
| 1010 | 1020 | fn readIntFd(fd: i32) !ErrInt { |
| 1011 | const file: File = .{ .handle = fd }; | |
| 1012 | return @intCast(file.deprecatedReader().readInt(u64, .little) catch return error.SystemResources); | |
| 1021 | var buffer: [8]u8 = undefined; | |
| 1022 | var fr: std.fs.File.Reader = .initMode(.{ .handle = fd }, &buffer, .streaming); | |
| 1023 | return @intCast(fr.interface.takeInt(u64, .little) catch return error.SystemResources); | |
| 1013 | 1024 | } |
| 1014 | 1025 | |
| 1015 | 1026 | const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); |
| ... | ... | @@ -1020,8 +1031,8 @@ const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); |
| 1020 | 1031 | /// Note: If the dir is the cwd, dir_buf should be empty (len = 0). |
| 1021 | 1032 | fn windowsCreateProcessPathExt( |
| 1022 | 1033 | allocator: mem.Allocator, |
| 1023 | dir_buf: *std.ArrayListUnmanaged(u16), | |
| 1024 | app_buf: *std.ArrayListUnmanaged(u16), | |
| 1034 | dir_buf: *ArrayList(u16), | |
| 1035 | app_buf: *ArrayList(u16), | |
| 1025 | 1036 | pathext: [:0]const u16, |
| 1026 | 1037 | cmd_line_cache: *WindowsCommandLineCache, |
| 1027 | 1038 | envp_ptr: ?[*]u16, |
| ... | ... | @@ -1504,7 +1515,7 @@ const WindowsCommandLineCache = struct { |
| 1504 | 1515 | /// Returns the absolute path of `cmd.exe` within the Windows system directory. |
| 1505 | 1516 | /// The caller owns the returned slice. |
| 1506 | 1517 | fn windowsCmdExePath(allocator: mem.Allocator) error{ OutOfMemory, Unexpected }![:0]u16 { |
| 1507 | var buf = try std.ArrayListUnmanaged(u16).initCapacity(allocator, 128); | |
| 1518 | var buf = try ArrayList(u16).initCapacity(allocator, 128); | |
| 1508 | 1519 | errdefer buf.deinit(allocator); |
| 1509 | 1520 | while (true) { |
| 1510 | 1521 | const unused_slice = buf.unusedCapacitySlice(); |
src/Compilation.zig+11-13| ... | ... | @@ -6215,19 +6215,20 @@ fn spawnZigRc( |
| 6215 | 6215 | return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) }); |
| 6216 | 6216 | }; |
| 6217 | 6217 | |
| 6218 | var poller = std.io.poll(comp.gpa, enum { stdout }, .{ | |
| 6218 | var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{ | |
| 6219 | 6219 | .stdout = child.stdout.?, |
| 6220 | .stderr = child.stderr.?, | |
| 6220 | 6221 | }); |
| 6221 | 6222 | defer poller.deinit(); |
| 6222 | 6223 | |
| 6223 | const stdout = poller.fifo(.stdout); | |
| 6224 | const stdout = poller.reader(.stdout); | |
| 6224 | 6225 | |
| 6225 | 6226 | poll: while (true) { |
| 6226 | while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) if (!try poller.poll()) break :poll; | |
| 6227 | var header: std.zig.Server.Message.Header = undefined; | |
| 6228 | assert(stdout.read(std.mem.asBytes(&header)) == @sizeOf(std.zig.Server.Message.Header)); | |
| 6229 | while (stdout.readableLength() < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 6230 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 6227 | const MessageHeader = std.zig.Server.Message.Header; | |
| 6228 | while (stdout.buffered().len < @sizeOf(MessageHeader)) if (!try poller.poll()) break :poll; | |
| 6229 | const header = stdout.takeStruct(MessageHeader, .little) catch unreachable; | |
| 6230 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 6231 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 6231 | 6232 | |
| 6232 | 6233 | switch (header.tag) { |
| 6233 | 6234 | // We expect exactly one ErrorBundle, and if any error_bundle header is |
| ... | ... | @@ -6250,13 +6251,10 @@ fn spawnZigRc( |
| 6250 | 6251 | }, |
| 6251 | 6252 | else => {}, // ignore other messages |
| 6252 | 6253 | } |
| 6253 | ||
| 6254 | stdout.discard(body.len); | |
| 6255 | 6254 | } |
| 6256 | 6255 | |
| 6257 | 6256 | // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace) |
| 6258 | const stderr_reader = child.stderr.?.deprecatedReader(); | |
| 6259 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); | |
| 6257 | const stderr = poller.reader(.stderr); | |
| 6260 | 6258 | |
| 6261 | 6259 | const term = child.wait() catch |err| { |
| 6262 | 6260 | return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) }); |
| ... | ... | @@ -6265,12 +6263,12 @@ fn spawnZigRc( |
| 6265 | 6263 | switch (term) { |
| 6266 | 6264 | .Exited => |code| { |
| 6267 | 6265 | if (code != 0) { |
| 6268 | log.err("zig rc failed with stderr:\n{s}", .{stderr}); | |
| 6266 | log.err("zig rc failed with stderr:\n{s}", .{stderr.buffered()}); | |
| 6269 | 6267 | return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); |
| 6270 | 6268 | } |
| 6271 | 6269 | }, |
| 6272 | 6270 | else => { |
| 6273 | log.err("zig rc terminated with stderr:\n{s}", .{stderr}); | |
| 6271 | log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()}); | |
| 6274 | 6272 | return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); |
| 6275 | 6273 | }, |
| 6276 | 6274 | } |
tools/docgen.zig-1| ... | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const fs = std.fs; |
| 5 | 5 | const process = std.process; |
| 6 | const ChildProcess = std.process.Child; | |
| 7 | 6 | const Progress = std.Progress; |
| 8 | 7 | const print = std.debug.print; |
| 9 | 8 | const mem = std.mem; |
tools/incr-check.zig+24-39| ... | ... | @@ -186,7 +186,7 @@ pub fn main() !void { |
| 186 | 186 | |
| 187 | 187 | try child.spawn(); |
| 188 | 188 | |
| 189 | var poller = std.io.poll(arena, Eval.StreamEnum, .{ | |
| 189 | var poller = std.Io.poll(arena, Eval.StreamEnum, .{ | |
| 190 | 190 | .stdout = child.stdout.?, |
| 191 | 191 | .stderr = child.stderr.?, |
| 192 | 192 | }); |
| ... | ... | @@ -247,19 +247,15 @@ const Eval = struct { |
| 247 | 247 | |
| 248 | 248 | fn check(eval: *Eval, poller: *Poller, update: Case.Update, prog_node: std.Progress.Node) !void { |
| 249 | 249 | const arena = eval.arena; |
| 250 | const Header = std.zig.Server.Message.Header; | |
| 251 | const stdout = poller.fifo(.stdout); | |
| 252 | const stderr = poller.fifo(.stderr); | |
| 250 | const stdout = poller.reader(.stdout); | |
| 251 | const stderr = poller.reader(.stderr); | |
| 253 | 252 | |
| 254 | 253 | poll: while (true) { |
| 255 | while (stdout.readableLength() < @sizeOf(Header)) { | |
| 256 | if (!(try poller.poll())) break :poll; | |
| 257 | } | |
| 258 | const header = stdout.reader().readStruct(Header) catch unreachable; | |
| 259 | while (stdout.readableLength() < header.bytes_len) { | |
| 260 | if (!(try poller.poll())) break :poll; | |
| 261 | } | |
| 262 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 254 | const Header = std.zig.Server.Message.Header; | |
| 255 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; | |
| 256 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 257 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 258 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 263 | 259 | |
| 264 | 260 | switch (header.tag) { |
| 265 | 261 | .error_bundle => { |
| ... | ... | @@ -277,8 +273,8 @@ const Eval = struct { |
| 277 | 273 | .string_bytes = try arena.dupe(u8, string_bytes), |
| 278 | 274 | .extra = extra_array, |
| 279 | 275 | }; |
| 280 | if (stderr.readableLength() > 0) { | |
| 281 | const stderr_data = try stderr.toOwnedSlice(); | |
| 276 | if (stderr.bufferedLen() > 0) { | |
| 277 | const stderr_data = try poller.toOwnedSlice(.stderr); | |
| 282 | 278 | if (eval.allow_stderr) { |
| 283 | 279 | std.log.info("error_bundle included stderr:\n{s}", .{stderr_data}); |
| 284 | 280 | } else { |
| ... | ... | @@ -289,15 +285,14 @@ const Eval = struct { |
| 289 | 285 | try eval.checkErrorOutcome(update, result_error_bundle); |
| 290 | 286 | } |
| 291 | 287 | // This message indicates the end of the update. |
| 292 | stdout.discard(body.len); | |
| 293 | 288 | return; |
| 294 | 289 | }, |
| 295 | 290 | .emit_digest => { |
| 296 | 291 | const EbpHdr = std.zig.Server.Message.EmitDigest; |
| 297 | 292 | const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); |
| 298 | 293 | _ = ebp_hdr; |
| 299 | if (stderr.readableLength() > 0) { | |
| 300 | const stderr_data = try stderr.toOwnedSlice(); | |
| 294 | if (stderr.bufferedLen() > 0) { | |
| 295 | const stderr_data = try poller.toOwnedSlice(.stderr); | |
| 301 | 296 | if (eval.allow_stderr) { |
| 302 | 297 | std.log.info("emit_digest included stderr:\n{s}", .{stderr_data}); |
| 303 | 298 | } else { |
| ... | ... | @@ -308,7 +303,6 @@ const Eval = struct { |
| 308 | 303 | if (eval.target.backend == .sema) { |
| 309 | 304 | try eval.checkSuccessOutcome(update, null, prog_node); |
| 310 | 305 | // This message indicates the end of the update. |
| 311 | stdout.discard(body.len); | |
| 312 | 306 | } |
| 313 | 307 | |
| 314 | 308 | const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; |
| ... | ... | @@ -323,21 +317,18 @@ const Eval = struct { |
| 323 | 317 | |
| 324 | 318 | try eval.checkSuccessOutcome(update, bin_path, prog_node); |
| 325 | 319 | // This message indicates the end of the update. |
| 326 | stdout.discard(body.len); | |
| 327 | 320 | }, |
| 328 | 321 | else => { |
| 329 | 322 | // Ignore other messages. |
| 330 | stdout.discard(body.len); | |
| 331 | 323 | }, |
| 332 | 324 | } |
| 333 | 325 | } |
| 334 | 326 | |
| 335 | if (stderr.readableLength() > 0) { | |
| 336 | const stderr_data = try stderr.toOwnedSlice(); | |
| 327 | if (stderr.bufferedLen() > 0) { | |
| 337 | 328 | if (eval.allow_stderr) { |
| 338 | std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr_data }); | |
| 329 | std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr.buffered() }); | |
| 339 | 330 | } else { |
| 340 | eval.fatal("update '{s}' failed:\n{s}", .{ update.name, stderr_data }); | |
| 331 | eval.fatal("update '{s}' failed:\n{s}", .{ update.name, stderr.buffered() }); | |
| 341 | 332 | } |
| 342 | 333 | } |
| 343 | 334 | |
| ... | ... | @@ -537,25 +528,19 @@ const Eval = struct { |
| 537 | 528 | fn end(eval: *Eval, poller: *Poller) !void { |
| 538 | 529 | requestExit(eval.child, eval); |
| 539 | 530 | |
| 540 | const Header = std.zig.Server.Message.Header; | |
| 541 | const stdout = poller.fifo(.stdout); | |
| 542 | const stderr = poller.fifo(.stderr); | |
| 531 | const stdout = poller.reader(.stdout); | |
| 532 | const stderr = poller.reader(.stderr); | |
| 543 | 533 | |
| 544 | 534 | poll: while (true) { |
| 545 | while (stdout.readableLength() < @sizeOf(Header)) { | |
| 546 | if (!(try poller.poll())) break :poll; | |
| 547 | } | |
| 548 | const header = stdout.reader().readStruct(Header) catch unreachable; | |
| 549 | while (stdout.readableLength() < header.bytes_len) { | |
| 550 | if (!(try poller.poll())) break :poll; | |
| 551 | } | |
| 552 | const body = stdout.readableSliceOfLen(header.bytes_len); | |
| 553 | stdout.discard(body.len); | |
| 535 | const Header = std.zig.Server.Message.Header; | |
| 536 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; | |
| 537 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 538 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | |
| 539 | stdout.toss(header.bytes_len); | |
| 554 | 540 | } |
| 555 | 541 | |
| 556 | if (stderr.readableLength() > 0) { | |
| 557 | const stderr_data = try stderr.toOwnedSlice(); | |
| 558 | eval.fatal("unexpected stderr:\n{s}", .{stderr_data}); | |
| 542 | if (stderr.bufferedLen() > 0) { | |
| 543 | eval.fatal("unexpected stderr:\n{s}", .{stderr.buffered()}); | |
| 559 | 544 | } |
| 560 | 545 | } |
| 561 | 546 |