authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-30 00:44:29+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-30 22:03:14-08:00
logf8828e543ab6331395ef4d9f9a2b53770028b6b2
treef5cc71453e3f38208645157ccab26596461ad08e
parentc2679feaaab26cc76fc381c61d49488a96f1f63c

std.Build: fully upgrade Step.Run to std.Io timing (and fix a typo)


1 files changed, 48 insertions(+), 60 deletions(-)

lib/std/Build/Step/Run.zig+48-60
......@@ -1587,9 +1587,13 @@ fn spawnChildAndCollect(
15871587 };
15881588
15891589 if (run.stdio == .zig_test) {
1590 var timer = try std.time.Timer.start();
1591 defer run.step.result_duration_ns = timer.read();
1592 try evalZigTest(run, spawn_options, options, fuzz_context);
1590 const started: Io.Clock.Timestamp = try .now(io, .awake);
1591 const result = evalZigTest(run, spawn_options, options, fuzz_context) catch |err| switch (err) {
1592 error.Canceled => |e| return e,
1593 else => |e| e,
1594 };
1595 run.step.result_duration_ns = @intCast((try started.untilNow(io)).raw.nanoseconds);
1596 try result;
15931597 return null;
15941598 } else {
15951599 const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;
......@@ -1602,10 +1606,14 @@ fn spawnChildAndCollect(
16021606 } else .no_color;
16031607 defer if (inherit) io.unlockStderr();
16041608 try setColorEnvironmentVariables(run, environ_map, terminal_mode);
1605 var timer = try std.time.Timer.start();
1606 const res = try evalGeneric(run, spawn_options);
1607 run.step.result_duration_ns = timer.read();
1608 return .{ .term = res.term, .stdout = res.stdout, .stderr = res.stderr };
1609
1610 const started: Io.Clock.Timestamp = try .now(io, .awake);
1611 const result = evalGeneric(run, spawn_options) catch |err| switch (err) {
1612 error.Canceled => |e| return e,
1613 else => |e| e,
1614 };
1615 run.step.result_duration_ns = @intCast((try started.untilNow(io)).raw.nanoseconds);
1616 return try result;
16091617 }
16101618}
16111619
......@@ -1861,9 +1869,7 @@ fn waitZigTest(
18611869
18621870 var active_test_index: ?u32 = null;
18631871
1864 // `null` means this host does not support `std.time.Timer`. This timer is `reset()` whenever we
1865 // change `active_test_index`, i.e. whenever a test starts or finishes.
1866 var timer: ?std.time.Timer = std.time.Timer.start() catch null;
1872 var last_update: Io.Clock.Timestamp = try .now(io, .awake);
18671873
18681874 var coverage_id: ?u64 = null;
18691875
......@@ -1871,16 +1877,27 @@ fn waitZigTest(
18711877 // test. For instance, if the test runner leaves this much time between us requesting a test to
18721878 // start and it acknowledging the test starting, we terminate the child and raise an error. This
18731879 // *should* never happen, but could in theory be caused by some very unlucky IB in a test.
1874 const response_timeout_ns: ?u64 = ns: {
1875 if (fuzz_context != null) break :ns null; // don't timeout fuzz tests
1876 break :ns @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);
1880 const response_timeout: ?Io.Clock.Duration = t: {
1881 if (fuzz_context != null) break :t null; // don't timeout fuzz tests
1882 const ns = @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);
1883 break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) };
18771884 };
1885 const test_timeout: ?Io.Clock.Duration = if (options.unit_test_timeout_ns) |ns| .{
1886 .clock = .awake,
1887 .raw = .fromNanoseconds(ns),
1888 } else null;
18781889
18791890 const stdout = multi_reader.reader(0);
18801891 const stderr = multi_reader.reader(1);
18811892 const Header = std.zig.Server.Message.Header;
18821893
18831894 while (true) {
1895 const timeout: Io.Timeout = t: {
1896 const opt_duration = if (active_test_index == null) response_timeout else test_timeout;
1897 const duration = opt_duration orelse break :t .none;
1898 break :t .{ .deadline = last_update.addDuration(duration) };
1899 };
1900
18841901 // This block is exited when `stdout` contains enough bytes for a `Header`.
18851902 header_ready: {
18861903 if (stdout.buffered().len >= @sizeOf(Header)) {
......@@ -1888,65 +1905,33 @@ fn waitZigTest(
18881905 break :header_ready;
18891906 }
18901907
1891 // Always `null` if `timer` is `null`.
1892 const opt_timeout_ns: ?u64 = ns: {
1893 if (timer == null) break :ns null;
1894 if (active_test_index == null) break :ns response_timeout_ns;
1895 break :ns options.unit_test_timeout_ns;
1896 };
1897
1898 const timeout: Io.Timeout = if (opt_timeout_ns) |timeout_ns| .{ .duration = .{
1899 .raw = .fromNanoseconds(timeout_ns -| timer.?.read()),
1900 .clock = .awake,
1901 } } else .none;
1902
19031908 multi_reader.fill(64, timeout) catch |err| switch (err) {
1904 error.Timeout, error.EndOfStream => return .{ .no_poll = .{
1909 error.Timeout => return .{ .timeout = .{
19051910 .active_test_index = active_test_index,
1906 .ns_elapsed = if (timer) |*t| t.read() else 0,
1911 .ns_elapsed = @intCast((try last_update.untilNow(io)).raw.nanoseconds),
1912 } },
1913 error.EndOfStream => return .{ .no_poll = .{
1914 .active_test_index = active_test_index,
1915 .ns_elapsed = @intCast((try last_update.untilNow(io)).raw.nanoseconds),
19071916 } },
1908 error.UnsupportedClock => {
1909 timer = null;
1910 continue;
1911 },
19121917 else => |e| return e,
19131918 };
19141919
1915 if (stdout.buffered().len >= @sizeOf(Header)) {
1916 // There wasn't a header before, but there is one after the `poll`.
1917 break :header_ready;
1918 }
1919
1920 if (opt_timeout_ns) |timeout_ns| {
1921 const cur_ns = timer.?.read();
1922 if (cur_ns >= timeout_ns) return .{ .timeout = .{
1923 .active_test_index = active_test_index,
1924 .ns_elapsed = cur_ns,
1925 } };
1926 }
19271920 continue;
19281921 }
19291922 // There is definitely a header available now -- read it.
19301923 const header = stdout.takeStruct(Header, .little) catch unreachable;
19311924
19321925 while (stdout.buffered().len < header.bytes_len) {
1933 const timeout: Io.Timeout = t: {
1934 const t = if (timer) |*t| t else break :t .none;
1935 if (response_timeout_ns) |timeout_ns| break :t .{ .duration = .{
1936 .raw = .fromNanoseconds(timeout_ns -| t.read()),
1937 .clock = .awake,
1938 } };
1939 break :t .none;
1940 };
19411926 multi_reader.fill(64, timeout) catch |err| switch (err) {
1942 error.Timeout, error.EndOfStream => return .{ .no_poll = .{
1927 error.Timeout => return .{ .timeout = .{
19431928 .active_test_index = active_test_index,
1944 .ns_elapsed = if (timer) |*t| t.read() else 0,
1929 .ns_elapsed = @intCast((try last_update.untilNow(io)).raw.nanoseconds),
1930 } },
1931 error.EndOfStream => return .{ .no_poll = .{
1932 .active_test_index = active_test_index,
1933 .ns_elapsed = @intCast((try last_update.untilNow(io)).raw.nanoseconds),
19451934 } },
1946 error.UnsupportedClock => {
1947 timer = null;
1948 continue;
1949 },
19501935 else => |e| return e,
19511936 };
19521937 }
......@@ -1991,13 +1976,13 @@ fn waitZigTest(
19911976 @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64));
19921977
19931978 active_test_index = null;
1994 if (timer) |*t| t.reset();
1979 last_update = try .now(io, .awake);
19951980
19961981 requestNextTest(io, child.stdin.?, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err };
19971982 },
19981983 .test_started => {
19991984 active_test_index = opt_metadata.*.?.next_index - 1;
2000 if (timer) |*t| t.reset();
1985 last_update = try .now(io, .awake);
20011986 },
20021987 .test_results => {
20031988 assert(fuzz_context == null);
......@@ -2040,7 +2025,10 @@ fn waitZigTest(
20402025 }
20412026
20422027 active_test_index = null;
2043 if (timer) |*t| md.ns_per_test[tr_hdr.index] = t.lap();
2028
2029 const now: Io.Clock.Timestamp = try .now(io, .awake);
2030 md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds);
2031 last_update = now;
20442032
20452033 requestNextTest(io, child.stdin.?, md, &sub_prog_node) catch |err| return .{ .write_failed = err };
20462034 },