| author | |
| committer | |
| log | 0b681ec6c39bf49a590ce2a8fa46e91c08426c43 |
| tree | 9e572431f48a4ecf0086b665137d90e27db5cb3e |
| parent | 7faf6be3535195b96b69fb9b8d12f16015d7ec36 |
| signature |
6 files changed, 202 insertions(+), 191 deletions(-)
lib/compiler/Maker/Step.zig+10-8| ... | ... | @@ -561,24 +561,26 @@ fn zigProcessUpdate(step_index: Configuration.Step.Index, maker: *Maker, zp: *Zi |
| 561 | 561 | var result: ?Path = null; |
| 562 | 562 | var eos_err: error{EndOfStream}!void = {}; |
| 563 | 563 | |
| 564 | const stdout = zp.multi_reader.fileReader(0); | |
| 564 | var client: std.zig.Client = .{ | |
| 565 | .in = zp.multi_reader.reader(0), | |
| 566 | .out = undefined, | |
| 567 | }; | |
| 565 | 568 | |
| 566 | 569 | while (true) { |
| 567 | const Header = std.zig.Server.Message.Header; | |
| 568 | const header = stdout.interface.takeStruct(Header, .little) catch |err| switch (err) { | |
| 569 | error.EndOfStream => break, | |
| 570 | error.ReadFailed => return stdout.err.?, | |
| 571 | }; | |
| 572 | const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) { | |
| 570 | const header = client.receiveMessageWithMultiReader(&zp.multi_reader, .none) catch |err| switch (err) { | |
| 571 | error.Timeout => unreachable, | |
| 573 | 572 | error.EndOfStream => |e| { |
| 573 | if (client.in.bufferedLen() == 0) break; | |
| 574 | 574 | // Better to report the crash with stderr below, but we set |
| 575 | 575 | // this in case the child exits successfully while violating |
| 576 | 576 | // this protocol. |
| 577 | 577 | eos_err = e; |
| 578 | 578 | break; |
| 579 | 579 | }, |
| 580 | error.ReadFailed => return stdout.err.?, | |
| 580 | else => |e| return e, | |
| 581 | 581 | }; |
| 582 | const body = client.in.take(header.bytes_len) catch unreachable; | |
| 583 | ||
| 582 | 584 | switch (header.tag) { |
| 583 | 585 | .zig_version => { |
| 584 | 586 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { |
lib/compiler/Maker/Step/Run.zig+37-121| ... | ... | @@ -384,13 +384,23 @@ fn waitZigTest( |
| 384 | 384 | var sub_prog_node: ?std.Progress.Node = null; |
| 385 | 385 | defer if (sub_prog_node) |n| n.end(); |
| 386 | 386 | |
| 387 | const stdout = multi_reader.reader(0); | |
| 388 | const stderr = multi_reader.reader(1); | |
| 389 | ||
| 390 | var stdin_writer = child.stdin.?.writerStreaming(io, &.{}); | |
| 391 | ||
| 392 | var client: std.zig.Client = .{ | |
| 393 | .in = stdout, | |
| 394 | .out = &stdin_writer.interface, | |
| 395 | }; | |
| 396 | ||
| 387 | 397 | if (opt_metadata.*) |*md| { |
| 388 | 398 | // Previous unit test process died or was killed; we're continuing where it left off |
| 389 | requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | |
| 399 | requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | |
| 390 | 400 | } else { |
| 391 | 401 | // Running unit tests normally |
| 392 | 402 | run.fuzz_tests.clearRetainingCapacity(); |
| 393 | sendMessage(io, child.stdin.?, .query_test_metadata) catch |err| return .{ .write_failed = err }; | |
| 403 | client.serveBodylessMessage(.query_test_metadata) catch |err| return .{ .write_failed = err }; | |
| 394 | 404 | } |
| 395 | 405 | |
| 396 | 406 | var active_test_index: ?u32 = null; |
| ... | ... | @@ -410,10 +420,6 @@ fn waitZigTest( |
| 410 | 420 | .raw = .fromNanoseconds(ns), |
| 411 | 421 | } else null; |
| 412 | 422 | |
| 413 | const stdout = multi_reader.reader(0); | |
| 414 | const stderr = multi_reader.reader(1); | |
| 415 | const Header = std.zig.Server.Message.Header; | |
| 416 | ||
| 417 | 423 | while (true) { |
| 418 | 424 | const timeout: Io.Timeout = t: { |
| 419 | 425 | const opt_duration = if (active_test_index == null) response_timeout else test_timeout; |
| ... | ... | @@ -421,46 +427,20 @@ fn waitZigTest( |
| 421 | 427 | break :t .{ .deadline = last_update.addDuration(duration) }; |
| 422 | 428 | }; |
| 423 | 429 | |
| 424 | // This block is exited when `stdout` contains enough bytes for a `Header`. | |
| 425 | header_ready: { | |
| 426 | if (stdout.buffered().len >= @sizeOf(Header)) { | |
| 427 | // We already have one, no need to poll! | |
| 428 | break :header_ready; | |
| 429 | } | |
| 430 | ||
| 431 | multi_reader.fill(64, timeout) catch |err| switch (err) { | |
| 432 | error.Timeout => return .{ .timeout = .{ | |
| 433 | .active_test_index = active_test_index, | |
| 434 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 435 | } }, | |
| 436 | error.EndOfStream => return .{ .no_poll = .{ | |
| 437 | .active_test_index = active_test_index, | |
| 438 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 439 | } }, | |
| 440 | else => |e| return e, | |
| 441 | }; | |
| 442 | ||
| 443 | continue; | |
| 444 | } | |
| 445 | // There is definitely a header available now -- read it. | |
| 446 | const header = stdout.takeStruct(Header, .little) catch unreachable; | |
| 447 | ||
| 448 | while (stdout.buffered().len < header.bytes_len) { | |
| 449 | multi_reader.fill(64, timeout) catch |err| switch (err) { | |
| 450 | error.Timeout => return .{ .timeout = .{ | |
| 451 | .active_test_index = active_test_index, | |
| 452 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 453 | } }, | |
| 454 | error.EndOfStream => return .{ .no_poll = .{ | |
| 455 | .active_test_index = active_test_index, | |
| 456 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 457 | } }, | |
| 458 | else => |e| return e, | |
| 459 | }; | |
| 460 | } | |
| 461 | ||
| 462 | const body = stdout.take(header.bytes_len) catch unreachable; | |
| 430 | const header = client.receiveMessageWithMultiReader(multi_reader, timeout) catch |err| switch (err) { | |
| 431 | error.Timeout => return .{ .timeout = .{ | |
| 432 | .active_test_index = active_test_index, | |
| 433 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 434 | } }, | |
| 435 | error.EndOfStream => return .{ .no_poll = .{ | |
| 436 | .active_test_index = active_test_index, | |
| 437 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 438 | } }, | |
| 439 | else => |e| return e, | |
| 440 | }; | |
| 441 | const body = client.in.take(header.bytes_len) catch unreachable; | |
| 463 | 442 | var body_r: std.Io.Reader = .fixed(body); |
| 443 | ||
| 464 | 444 | switch (header.tag) { |
| 465 | 445 | .zig_version => { |
| 466 | 446 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail( |
| ... | ... | @@ -500,7 +480,7 @@ fn waitZigTest( |
| 500 | 480 | active_test_index = null; |
| 501 | 481 | last_update = .now(io, .awake); |
| 502 | 482 | |
| 503 | requestNextTest(io, child.stdin.?, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; | |
| 483 | requestNextTest(&client, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; | |
| 504 | 484 | }, |
| 505 | 485 | .test_started => { |
| 506 | 486 | active_test_index = opt_metadata.*.?.next_index - 1; |
| ... | ... | @@ -551,7 +531,7 @@ fn waitZigTest( |
| 551 | 531 | md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds); |
| 552 | 532 | last_update = now; |
| 553 | 533 | |
| 554 | requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | |
| 534 | requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err }; | |
| 555 | 535 | }, |
| 556 | 536 | else => {}, // ignore other messages |
| 557 | 537 | } |
| ... | ... | @@ -697,17 +677,18 @@ const FuzzTestRunner = struct { |
| 697 | 677 | |
| 698 | 678 | for (0.., f.instances) |id, *instance| { |
| 699 | 679 | const id32: u32 = @intCast(id); |
| 680 | var writer = instance.child.stdin.?.writerStreaming(io, &.{}); | |
| 681 | const client: std.zig.Client = .{ | |
| 682 | .in = undefined, | |
| 683 | .out = &writer.interface, | |
| 684 | }; | |
| 700 | 685 | (switch (f.ctx.fuzz.mode) { |
| 701 | .forever => sendRunFuzzTestMessage( | |
| 702 | io, | |
| 703 | instance.child.stdin.?, | |
| 686 | .forever => client.serveRunFuzzTestMessage( | |
| 704 | 687 | run.fuzz_tests.items, |
| 705 | 688 | .forever, |
| 706 | 689 | id32, |
| 707 | 690 | ), |
| 708 | .limit => |limit| sendRunFuzzTestMessage( | |
| 709 | io, | |
| 710 | instance.child.stdin.?, | |
| 691 | .limit => |limit| client.serveRunFuzzTestMessage( | |
| 711 | 692 | run.fuzz_tests.items, |
| 712 | 693 | .iterations, |
| 713 | 694 | limit.amount, |
| ... | ... | @@ -1315,7 +1296,7 @@ pub const CachedTestMetadata = struct { |
| 1315 | 1296 | } |
| 1316 | 1297 | }; |
| 1317 | 1298 | |
| 1318 | fn requestNextTest(io: Io, in: Io.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { | |
| 1299 | fn requestNextTest(client: *std.zig.Client, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { | |
| 1319 | 1300 | while (metadata.next_index < metadata.names.len) { |
| 1320 | 1301 | const i = metadata.next_index; |
| 1321 | 1302 | metadata.next_index += 1; |
| ... | ... | @@ -1326,76 +1307,11 @@ fn requestNextTest(io: Io, in: Io.File, metadata: *TestMetadata, sub_prog_node: |
| 1326 | 1307 | if (sub_prog_node.*) |n| n.end(); |
| 1327 | 1308 | sub_prog_node.* = metadata.prog_node.start(name, 0); |
| 1328 | 1309 | |
| 1329 | try sendRunTestMessage(io, in, .run_test, i); | |
| 1310 | try client.serveRunTest(i); | |
| 1330 | 1311 | return; |
| 1331 | 1312 | } else { |
| 1332 | 1313 | metadata.next_index = std.math.maxInt(u32); // indicate that all tests are done |
| 1333 | try sendMessage(io, in, .exit); | |
| 1334 | } | |
| 1335 | } | |
| 1336 | ||
| 1337 | fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { | |
| 1338 | const header: std.zig.Client.Message.Header = .{ | |
| 1339 | .tag = tag, | |
| 1340 | .bytes_len = 0, | |
| 1341 | }; | |
| 1342 | var w = file.writerStreaming(io, &.{}); | |
| 1343 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | |
| 1344 | error.WriteFailed => return w.err.?, | |
| 1345 | }; | |
| 1346 | } | |
| 1347 | ||
| 1348 | fn sendRunTestMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag, index: u32) !void { | |
| 1349 | const header: std.zig.Client.Message.Header = .{ | |
| 1350 | .tag = tag, | |
| 1351 | .bytes_len = 4, | |
| 1352 | }; | |
| 1353 | var w = file.writerStreaming(io, &.{}); | |
| 1354 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | |
| 1355 | error.WriteFailed => return w.err.?, | |
| 1356 | }; | |
| 1357 | w.interface.writeInt(u32, index, .little) catch |err| switch (err) { | |
| 1358 | error.WriteFailed => return w.err.?, | |
| 1359 | }; | |
| 1360 | } | |
| 1361 | ||
| 1362 | fn sendRunFuzzTestMessage( | |
| 1363 | io: Io, | |
| 1364 | file: Io.File, | |
| 1365 | test_names: []const []const u8, | |
| 1366 | kind: std.Build.abi.fuzz.LimitKind, | |
| 1367 | amount_or_instance: u64, | |
| 1368 | ) !void { | |
| 1369 | const header: std.zig.Client.Message.Header = .{ | |
| 1370 | .tag = .start_fuzzing, | |
| 1371 | .bytes_len = 1 + 8 + 4 + count: { | |
| 1372 | var c: u32 = @intCast(test_names.len * 4); | |
| 1373 | for (test_names) |name| { | |
| 1374 | c += @intCast(name.len); | |
| 1375 | } | |
| 1376 | break :count c; | |
| 1377 | }, | |
| 1378 | }; | |
| 1379 | var w = file.writerStreaming(io, &.{}); | |
| 1380 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | |
| 1381 | error.WriteFailed => return w.err.?, | |
| 1382 | }; | |
| 1383 | w.interface.writeByte(@backingInt(kind)) catch |err| switch (err) { | |
| 1384 | error.WriteFailed => return w.err.?, | |
| 1385 | }; | |
| 1386 | w.interface.writeInt(u64, amount_or_instance, .little) catch |err| switch (err) { | |
| 1387 | error.WriteFailed => return w.err.?, | |
| 1388 | }; | |
| 1389 | w.interface.writeInt(u32, @intCast(test_names.len), .little) catch |err| switch (err) { | |
| 1390 | error.WriteFailed => return w.err.?, | |
| 1391 | }; | |
| 1392 | for (test_names) |test_name| { | |
| 1393 | w.interface.writeInt(u32, @intCast(test_name.len), .little) catch |err| switch (err) { | |
| 1394 | error.WriteFailed => return w.err.?, | |
| 1395 | }; | |
| 1396 | w.interface.writeAll(test_name) catch |err| switch (err) { | |
| 1397 | error.WriteFailed => return w.err.?, | |
| 1398 | }; | |
| 1314 | try client.serveBodylessMessage(.exit); | |
| 1399 | 1315 | } |
| 1400 | 1316 | } |
| 1401 | 1317 |
lib/compiler/std-docs.zig+21-22| ... | ... | @@ -346,29 +346,39 @@ fn buildWasmBinary( |
| 346 | 346 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); |
| 347 | 347 | defer multi_reader.deinit(); |
| 348 | 348 | |
| 349 | try sendMessage(io, child.stdin.?, .update); | |
| 350 | try sendMessage(io, child.stdin.?, .exit); | |
| 349 | const stdout = multi_reader.reader(0); | |
| 350 | ||
| 351 | var stdin_buffer: [256]u8 = undefined; | |
| 352 | var stdin_writer = child.stdin.?.writerStreaming(io, &stdin_buffer); | |
| 353 | ||
| 354 | var client: std.zig.Client = .{ | |
| 355 | .in = stdout, | |
| 356 | .out = &stdin_writer.interface, | |
| 357 | }; | |
| 358 | ||
| 359 | try client.serveMessageHeader(.{ .tag = .update, .bytes_len = 0 }); | |
| 360 | try client.serveMessageHeader(.{ .tag = .exit, .bytes_len = 0 }); | |
| 361 | try client.out.flush(); | |
| 351 | 362 | |
| 352 | 363 | var result: ?Cache.Path = null; |
| 353 | 364 | var result_error_bundle = std.zig.ErrorBundle.empty; |
| 354 | 365 | |
| 355 | const stdout = multi_reader.fileReader(0); | |
| 356 | const MessageHeader = std.zig.Server.Message.Header; | |
| 357 | ||
| 358 | 366 | var eos_err: error{EndOfStream}!void = {}; |
| 359 | 367 | |
| 360 | 368 | while (true) { |
| 361 | const header = stdout.interface.takeStruct(MessageHeader, .little) catch |err| switch (err) { | |
| 362 | error.EndOfStream => break, | |
| 363 | error.ReadFailed => return stdout.err.?, | |
| 364 | }; | |
| 365 | const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) { | |
| 369 | const header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { | |
| 370 | error.Timeout => unreachable, | |
| 366 | 371 | error.EndOfStream => |e| { |
| 372 | if (client.in.bufferedLen() == 0) break; | |
| 373 | // Better to report the crash with stderr below, but we set | |
| 374 | // this in case the child exits successfully while violating | |
| 375 | // this protocol. | |
| 367 | 376 | eos_err = e; |
| 368 | 377 | break; |
| 369 | 378 | }, |
| 370 | error.ReadFailed => return stdout.err.?, | |
| 379 | else => |e| return e, | |
| 371 | 380 | }; |
| 381 | const body = client.in.take(header.bytes_len) catch unreachable; | |
| 372 | 382 | |
| 373 | 383 | switch (header.tag) { |
| 374 | 384 | .zig_version => { |
| ... | ... | @@ -435,17 +445,6 @@ fn buildWasmBinary( |
| 435 | 445 | }; |
| 436 | 446 | } |
| 437 | 447 | |
| 438 | fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void { | |
| 439 | const header: std.zig.Client.Message.Header = .{ | |
| 440 | .tag = tag, | |
| 441 | .bytes_len = 0, | |
| 442 | }; | |
| 443 | var w = file.writer(io, &.{}); | |
| 444 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | |
| 445 | error.WriteFailed => return w.err.?, | |
| 446 | }; | |
| 447 | } | |
| 448 | ||
| 449 | 448 | fn openBrowserTab(io: Io, url: []const u8) !void { |
| 450 | 449 | // Until https://github.com/ziglang/zig/issues/19205 is implemented, we |
| 451 | 450 | // spawn and then leak a concurrent task for this child process. |
lib/std/zig/Client.zig+88-2| ... | ... | @@ -1,3 +1,17 @@ |
| 1 | const Client = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const Io = std.Io; | |
| 5 | const Allocator = std.mem.Allocator; | |
| 6 | const assert = std.debug.assert; | |
| 7 | const OutMessage = std.zig.Client.Message; | |
| 8 | const InMessage = std.zig.Server.Message; | |
| 9 | const Reader = Io.Reader; | |
| 10 | const Writer = Io.Writer; | |
| 11 | ||
| 12 | in: *Reader, | |
| 13 | out: *Writer, | |
| 14 | ||
| 1 | 15 | pub const Message = struct { |
| 2 | 16 | pub const Header = extern struct { |
| 3 | 17 | tag: Tag, |
| ... | ... | @@ -50,7 +64,79 @@ pub const Message = struct { |
| 50 | 64 | }; |
| 51 | 65 | |
| 52 | 66 | comptime { |
| 53 | const std = @import("std"); | |
| 54 | std.debug.assert(@sizeOf(std.Build.abi.fuzz.LimitKind) == 1); | |
| 67 | assert(@sizeOf(std.Build.abi.fuzz.LimitKind) == 1); | |
| 55 | 68 | } |
| 56 | 69 | }; |
| 70 | ||
| 71 | pub fn receiveMessage(c: *const Client) Reader.Error!InMessage.Header { | |
| 72 | return c.in.takeStruct(InMessage.Header, .little); | |
| 73 | } | |
| 74 | ||
| 75 | /// Assumes that `c.in` is a reader in `multi_reader`. | |
| 76 | /// Guarantees that the response body will be buffered in `c.in` on success. | |
| 77 | pub fn receiveMessageWithMultiReader( | |
| 78 | c: *Client, | |
| 79 | multi_reader: *Io.File.MultiReader, | |
| 80 | timeout: Io.Timeout, | |
| 81 | ) (Io.File.MultiReader.Error || Io.Timeout.Error)!InMessage.Header { | |
| 82 | while (c.in.bufferedLen() < @sizeOf(InMessage.Header)) { | |
| 83 | multi_reader.fill(64, timeout) catch |err| switch (err) { | |
| 84 | error.Canceled, | |
| 85 | error.Timeout, | |
| 86 | error.ConcurrencyUnavailable, | |
| 87 | error.EndOfStream, | |
| 88 | => |e| return e, | |
| 89 | }; | |
| 90 | } | |
| 91 | const header = c.in.takeStruct(InMessage.Header, .little) catch unreachable; | |
| 92 | while (c.in.bufferedLen() < header.bytes_len) { | |
| 93 | try multi_reader.fill(header.bytes_len - c.in.bufferedLen(), timeout); | |
| 94 | } | |
| 95 | try multi_reader.checkAnyError(); | |
| 96 | return header; | |
| 97 | } | |
| 98 | ||
| 99 | /// Don't forget to flush! | |
| 100 | pub fn serveMessageHeader(c: *const Client, header: OutMessage.Header) Writer.Error!void { | |
| 101 | try c.out.writeStruct(header, .little); | |
| 102 | } | |
| 103 | ||
| 104 | pub fn serveBodylessMessage(c: *const Client, tag: OutMessage.Tag) Writer.Error!void { | |
| 105 | try c.serveMessageHeader(.{ .tag = tag, .bytes_len = 0 }); | |
| 106 | try c.out.flush(); | |
| 107 | } | |
| 108 | ||
| 109 | pub fn serveRunTest(c: *const Client, index: u32) !void { | |
| 110 | try c.serveMessageHeader(.{ | |
| 111 | .tag = .run_test, | |
| 112 | .bytes_len = @sizeOf(u32), | |
| 113 | }); | |
| 114 | try c.out.writeInt(u32, index, .little); | |
| 115 | try c.out.flush(); | |
| 116 | } | |
| 117 | ||
| 118 | pub fn serveRunFuzzTestMessage( | |
| 119 | c: *const Client, | |
| 120 | test_names: []const []const u8, | |
| 121 | kind: std.Build.abi.fuzz.LimitKind, | |
| 122 | amount_or_instance: u64, | |
| 123 | ) !void { | |
| 124 | try c.serveMessageHeader(.{ | |
| 125 | .tag = .start_fuzzing, | |
| 126 | .bytes_len = 1 + 8 + 4 + count: { | |
| 127 | var bytes_len: u32 = @intCast(test_names.len * 4); | |
| 128 | for (test_names) |name| { | |
| 129 | bytes_len += @intCast(name.len); | |
| 130 | } | |
| 131 | break :count bytes_len; | |
| 132 | }, | |
| 133 | }); | |
| 134 | try c.out.writeByte(@backingInt(kind)); | |
| 135 | try c.out.writeInt(u64, amount_or_instance, .little); | |
| 136 | try c.out.writeInt(u32, @intCast(test_names.len), .little); | |
| 137 | for (test_names) |test_name| { | |
| 138 | try c.out.writeInt(u32, @intCast(test_name.len), .little); | |
| 139 | try c.out.writeAll(test_name); | |
| 140 | } | |
| 141 | try c.out.flush(); | |
| 142 | } |
src/Compilation.zig+12-8| ... | ... | @@ -6006,26 +6006,30 @@ fn spawnZigRc( |
| 6006 | 6006 | multi_reader.init(gpa, io, multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? }); |
| 6007 | 6007 | defer multi_reader.deinit(); |
| 6008 | 6008 | |
| 6009 | const stdout = multi_reader.fileReader(0); | |
| 6010 | const MessageHeader = std.zig.Server.Message.Header; | |
| 6009 | const stdout = multi_reader.reader(0); | |
| 6011 | 6010 | |
| 6012 | 6011 | var eos_err: error{EndOfStream}!void = {}; |
| 6013 | 6012 | |
| 6013 | var client: std.zig.Client = .{ | |
| 6014 | .in = stdout, | |
| 6015 | .out = undefined, | |
| 6016 | }; | |
| 6017 | ||
| 6014 | 6018 | while (true) { |
| 6015 | const header = stdout.interface.takeStruct(MessageHeader, .little) catch |err| switch (err) { | |
| 6016 | error.EndOfStream => break, | |
| 6017 | error.ReadFailed => return stdout.err.?, | |
| 6018 | }; | |
| 6019 | const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) { | |
| 6019 | const header = client.receiveMessageWithMultiReader(&multi_reader, .none) catch |err| switch (err) { | |
| 6020 | error.Timeout => unreachable, | |
| 6020 | 6021 | error.EndOfStream => |e| { |
| 6022 | if (client.in.bufferedLen() == 0) break; | |
| 6021 | 6023 | // Better to report the crash with stderr below, but we set |
| 6022 | 6024 | // this in case the child exits successfully while violating |
| 6023 | 6025 | // this protocol. |
| 6024 | 6026 | eos_err = e; |
| 6025 | 6027 | break; |
| 6026 | 6028 | }, |
| 6027 | error.ReadFailed => return stdout.err.?, | |
| 6029 | else => |e| return e, | |
| 6028 | 6030 | }; |
| 6031 | const body = client.in.take(header.bytes_len) catch unreachable; | |
| 6032 | ||
| 6029 | 6033 | switch (header.tag) { |
| 6030 | 6034 | // We expect exactly one ErrorBundle, and if any error_bundle header is |
| 6031 | 6035 | // sent then it's a fatal error. |
tools/incr-check.zig+34-30| ... | ... | @@ -305,21 +305,23 @@ const Eval = struct { |
| 305 | 305 | |
| 306 | 306 | fn check(eval: *Eval, mr: *Io.File.MultiReader, update: Case.Update, prog_node: std.Progress.Node) !void { |
| 307 | 307 | const arena = eval.arena; |
| 308 | const stdout = mr.fileReader(0); | |
| 309 | const stderr = &mr.fileReader(1).interface; | |
| 310 | const Header = std.zig.Server.Message.Header; | |
| 308 | const stdout = mr.reader(0); | |
| 309 | const stderr = mr.reader(1); | |
| 310 | ||
| 311 | var client: std.zig.Client = .{ | |
| 312 | .in = stdout, | |
| 313 | .out = undefined, | |
| 314 | }; | |
| 311 | 315 | |
| 312 | 316 | while (true) { |
| 313 | const header = stdout.interface.takeStruct(Header, .little) catch |err| switch (err) { | |
| 314 | error.EndOfStream => break, | |
| 315 | error.ReadFailed => return stdout.err.?, | |
| 316 | }; | |
| 317 | const body = stdout.interface.take(header.bytes_len) catch |err| switch (err) { | |
| 317 | const header = client.receiveMessageWithMultiReader(mr, .none) catch |err| switch (err) { | |
| 318 | error.Timeout => unreachable, | |
| 318 | 319 | // If this panic triggers it might be helpful to rework this |
| 319 | 320 | // code to print the stderr from the abnormally terminated child. |
| 320 | 321 | error.EndOfStream => @panic("unexpected mid-message end of stream"), |
| 321 | error.ReadFailed => return stdout.err.?, | |
| 322 | else => |e| return e, | |
| 322 | 323 | }; |
| 324 | const body = client.in.take(header.bytes_len) catch unreachable; | |
| 323 | 325 | |
| 324 | 326 | switch (header.tag) { |
| 325 | 327 | .error_bundle => { |
| ... | ... | @@ -605,12 +607,13 @@ const Eval = struct { |
| 605 | 607 | |
| 606 | 608 | fn requestUpdate(eval: *Eval) !void { |
| 607 | 609 | const io = eval.io; |
| 608 | const header: std.zig.Client.Message.Header = .{ | |
| 609 | .tag = .update, | |
| 610 | .bytes_len = 0, | |
| 610 | ||
| 611 | var w = eval.child.stdin.?.writerStreaming(io, &.{}); | |
| 612 | var client: std.zig.Client = .{ | |
| 613 | .in = undefined, | |
| 614 | .out = &w.interface, | |
| 611 | 615 | }; |
| 612 | var w = eval.child.stdin.?.writer(io, &.{}); | |
| 613 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | |
| 616 | client.serveBodylessMessage(.update) catch |err| switch (err) { | |
| 614 | 617 | error.WriteFailed => return w.err.?, |
| 615 | 618 | }; |
| 616 | 619 | } |
| ... | ... | @@ -618,22 +621,23 @@ const Eval = struct { |
| 618 | 621 | fn end(eval: *Eval, mr: *Io.File.MultiReader) !void { |
| 619 | 622 | requestExit(eval.child, eval); |
| 620 | 623 | |
| 621 | const stdout = mr.fileReader(0); | |
| 622 | const Header = std.zig.Server.Message.Header; | |
| 624 | var client: std.zig.Client = .{ | |
| 625 | .in = mr.reader(0), | |
| 626 | .out = undefined, | |
| 627 | }; | |
| 623 | 628 | |
| 624 | 629 | while (true) { |
| 625 | const header = stdout.interface.takeStruct(Header, .little) catch |err| switch (err) { | |
| 626 | error.EndOfStream => break, | |
| 627 | error.ReadFailed => return stdout.err.?, | |
| 628 | }; | |
| 629 | stdout.interface.discardAll(header.bytes_len) catch |err| switch (err) { | |
| 630 | error.ReadFailed => return stdout.err.?, | |
| 631 | error.EndOfStream => |e| return e, | |
| 630 | const header = client.receiveMessageWithMultiReader(mr, .none) catch |err| switch (err) { | |
| 631 | error.Timeout => unreachable, | |
| 632 | error.EndOfStream => |e| { | |
| 633 | if (client.in.bufferedLen() == 0) break; | |
| 634 | return e; | |
| 635 | }, | |
| 636 | else => |e| return e, | |
| 632 | 637 | }; |
| 638 | try client.in.discardAll(header.bytes_len); | |
| 633 | 639 | } |
| 634 | 640 | |
| 635 | try mr.fillRemaining(.none); | |
| 636 | ||
| 637 | 641 | const stderr = mr.reader(1).buffered(); |
| 638 | 642 | if (stderr.len > 0) eval.fatal("unexpected stderr:\n{s}", .{stderr}); |
| 639 | 643 | } |
| ... | ... | @@ -899,12 +903,12 @@ fn requestExit(child: *std.process.Child, eval: *Eval) void { |
| 899 | 903 | if (child.stdin == null) return; |
| 900 | 904 | const io = eval.io; |
| 901 | 905 | |
| 902 | const header: std.zig.Client.Message.Header = .{ | |
| 903 | .tag = .exit, | |
| 904 | .bytes_len = 0, | |
| 906 | var w = eval.child.stdin.?.writerStreaming(io, &.{}); | |
| 907 | var client: std.zig.Client = .{ | |
| 908 | .in = undefined, | |
| 909 | .out = &w.interface, | |
| 905 | 910 | }; |
| 906 | var w = eval.child.stdin.?.writer(io, &.{}); | |
| 907 | w.interface.writeStruct(header, .little) catch |err| switch (err) { | |
| 911 | client.serveBodylessMessage(.exit) catch |err| switch (err) { | |
| 908 | 912 | error.WriteFailed => switch (w.err.?) { |
| 909 | 913 | error.BrokenPipe => {}, |
| 910 | 914 | else => |e| eval.fatal("failed to send exit: {t}", .{e}), |