| author | |
| committer | |
| log | e5e4602b181360d20bbba56e9f15734c929d2a2f |
| tree | f124eb04f53acf6b8029ddaef027a21c78f934d2 |
| parent | 7aae7dd3f4d4b85837369fa591e566ae812f87dc |
| parent | fe5da36aa3b1dbeb02276c6628640c68a5922191 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/3108623 files changed, 272 insertions(+), 406 deletions(-)
lib/compiler/aro/aro/Compilation.zig+1-1| ... | ... | @@ -107,7 +107,7 @@ pub const Environment = struct { |
| 107 | 107 | if (parsed > max_timestamp) return error.InvalidEpoch; |
| 108 | 108 | return .{ .provided = parsed }; |
| 109 | 109 | } else { |
| 110 | const timestamp = try Io.Clock.real.now(io); | |
| 110 | const timestamp = Io.Clock.real.now(io); | |
| 111 | 111 | const seconds = std.math.cast(u64, timestamp.toSeconds()) orelse return error.InvalidEpoch; |
| 112 | 112 | return .{ .system = std.math.clamp(seconds, 0, max_timestamp) }; |
| 113 | 113 | } |
lib/compiler/aro/aro/Preprocessor.zig+1-1| ... | ... | @@ -301,7 +301,7 @@ pub fn init(comp: *Compilation, source_epoch: SourceEpoch) Preprocessor { |
| 301 | 301 | /// Initialize Preprocessor with builtin macros. |
| 302 | 302 | pub fn initDefault(comp: *Compilation) !Preprocessor { |
| 303 | 303 | const source_epoch: SourceEpoch = comp.environment.sourceEpoch(comp.io) catch |er| switch (er) { |
| 304 | error.InvalidEpoch, error.UnsupportedClock, error.Unexpected => blk: { | |
| 304 | error.InvalidEpoch => blk: { | |
| 305 | 305 | const diagnostic: Diagnostic = .invalid_source_epoch; |
| 306 | 306 | try comp.diagnostics.add(.{ .text = diagnostic.fmt, .kind = diagnostic.kind, .opt = diagnostic.opt, .location = null }); |
| 307 | 307 | break :blk .default; |
lib/compiler/build_runner.zig+1-1| ... | ... | @@ -548,7 +548,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 548 | 548 | break :w try .init(graph.cache.cwd); |
| 549 | 549 | }; |
| 550 | 550 | |
| 551 | const now = Io.Clock.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err}); | |
| 551 | const now = Io.Clock.Timestamp.now(io, .awake); | |
| 552 | 552 | |
| 553 | 553 | run.web_server = if (webui_listen) |listen_address| ws: { |
| 554 | 554 | if (builtin.single_threaded) unreachable; // `fatal` above |
lib/std/Build/Step.zig+11-8| ... | ... | @@ -266,16 +266,19 @@ pub fn init(options: StepOptions) Step { |
| 266 | 266 | /// here. |
| 267 | 267 | pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void { |
| 268 | 268 | const arena = s.owner.allocator; |
| 269 | const graph = s.owner.graph; | |
| 270 | const io = graph.io; | |
| 269 | 271 | |
| 270 | var timer: ?std.time.Timer = t: { | |
| 271 | if (!s.owner.graph.time_report) break :t null; | |
| 272 | var start_ts: ?Io.Timestamp = t: { | |
| 273 | if (!graph.time_report) break :t null; | |
| 272 | 274 | if (s.id == .compile) break :t null; |
| 273 | 275 | if (s.id == .run and s.cast(Run).?.stdio == .zig_test) break :t null; |
| 274 | break :t std.time.Timer.start() catch @panic("--time-report not supported on this host"); | |
| 276 | break :t Io.Clock.awake.now(io); | |
| 275 | 277 | }; |
| 276 | 278 | const make_result = s.makeFn(s, options); |
| 277 | if (timer) |*t| { | |
| 278 | options.web_server.?.updateTimeReportGeneric(s, t.read()); | |
| 279 | if (start_ts) |*ts| { | |
| 280 | const duration = ts.untilNow(io, .awake); | |
| 281 | options.web_server.?.updateTimeReportGeneric(s, duration); | |
| 279 | 282 | } |
| 280 | 283 | |
| 281 | 284 | make_result catch |err| switch (err) { |
| ... | ... | @@ -534,7 +537,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build. |
| 534 | 537 | const arena = b.allocator; |
| 535 | 538 | const io = b.graph.io; |
| 536 | 539 | |
| 537 | var timer = try std.time.Timer.start(); | |
| 540 | const start_ts = Io.Clock.awake.now(io); | |
| 538 | 541 | |
| 539 | 542 | try sendMessage(io, zp.child.stdin.?, .update); |
| 540 | 543 | if (!watch) try sendMessage(io, zp.child.stdin.?, .exit); |
| ... | ... | @@ -637,7 +640,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build. |
| 637 | 640 | .compile = s.cast(Step.Compile).?, |
| 638 | 641 | .use_llvm = tr.flags.use_llvm, |
| 639 | 642 | .stats = tr.stats, |
| 640 | .ns_total = timer.read(), | |
| 643 | .ns_total = @intCast(start_ts.untilNow(io, .awake).toNanoseconds()), | |
| 641 | 644 | .llvm_pass_timings_len = tr.llvm_pass_timings_len, |
| 642 | 645 | .files_len = tr.files_len, |
| 643 | 646 | .decls_len = tr.decls_len, |
| ... | ... | @@ -648,7 +651,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build. |
| 648 | 651 | } |
| 649 | 652 | } |
| 650 | 653 | |
| 651 | s.result_duration_ns = timer.read(); | |
| 654 | s.result_duration_ns = @intCast(start_ts.untilNow(io, .awake).toNanoseconds()); | |
| 652 | 655 | |
| 653 | 656 | const stderr_contents = zp.multi_reader.reader(1).buffered(); |
| 654 | 657 | if (stderr_contents.len > 0) { |
lib/std/Build/Step/Run.zig+13-13| ... | ... | @@ -1587,12 +1587,12 @@ fn spawnChildAndCollect( |
| 1587 | 1587 | }; |
| 1588 | 1588 | |
| 1589 | 1589 | if (run.stdio == .zig_test) { |
| 1590 | const started: Io.Clock.Timestamp = try .now(io, .awake); | |
| 1590 | const started: Io.Clock.Timestamp = .now(io, .awake); | |
| 1591 | 1591 | const result = evalZigTest(run, spawn_options, options, fuzz_context) catch |err| switch (err) { |
| 1592 | 1592 | error.Canceled => |e| return e, |
| 1593 | 1593 | else => |e| e, |
| 1594 | 1594 | }; |
| 1595 | run.step.result_duration_ns = @intCast((try started.untilNow(io)).raw.nanoseconds); | |
| 1595 | run.step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds); | |
| 1596 | 1596 | try result; |
| 1597 | 1597 | return null; |
| 1598 | 1598 | } else { |
| ... | ... | @@ -1607,12 +1607,12 @@ fn spawnChildAndCollect( |
| 1607 | 1607 | defer if (inherit) io.unlockStderr(); |
| 1608 | 1608 | try setColorEnvironmentVariables(run, environ_map, terminal_mode); |
| 1609 | 1609 | |
| 1610 | const started: Io.Clock.Timestamp = try .now(io, .awake); | |
| 1610 | const started: Io.Clock.Timestamp = .now(io, .awake); | |
| 1611 | 1611 | const result = evalGeneric(run, spawn_options) catch |err| switch (err) { |
| 1612 | 1612 | error.Canceled => |e| return e, |
| 1613 | 1613 | else => |e| e, |
| 1614 | 1614 | }; |
| 1615 | run.step.result_duration_ns = @intCast((try started.untilNow(io)).raw.nanoseconds); | |
| 1615 | run.step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds); | |
| 1616 | 1616 | return try result; |
| 1617 | 1617 | } |
| 1618 | 1618 | } |
| ... | ... | @@ -1869,7 +1869,7 @@ fn waitZigTest( |
| 1869 | 1869 | |
| 1870 | 1870 | var active_test_index: ?u32 = null; |
| 1871 | 1871 | |
| 1872 | var last_update: Io.Clock.Timestamp = try .now(io, .awake); | |
| 1872 | var last_update: Io.Clock.Timestamp = .now(io, .awake); | |
| 1873 | 1873 | |
| 1874 | 1874 | var coverage_id: ?u64 = null; |
| 1875 | 1875 | |
| ... | ... | @@ -1908,11 +1908,11 @@ fn waitZigTest( |
| 1908 | 1908 | multi_reader.fill(64, timeout) catch |err| switch (err) { |
| 1909 | 1909 | error.Timeout => return .{ .timeout = .{ |
| 1910 | 1910 | .active_test_index = active_test_index, |
| 1911 | .ns_elapsed = @intCast((try last_update.untilNow(io)).raw.nanoseconds), | |
| 1911 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 1912 | 1912 | } }, |
| 1913 | 1913 | error.EndOfStream => return .{ .no_poll = .{ |
| 1914 | 1914 | .active_test_index = active_test_index, |
| 1915 | .ns_elapsed = @intCast((try last_update.untilNow(io)).raw.nanoseconds), | |
| 1915 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 1916 | 1916 | } }, |
| 1917 | 1917 | else => |e| return e, |
| 1918 | 1918 | }; |
| ... | ... | @@ -1926,11 +1926,11 @@ fn waitZigTest( |
| 1926 | 1926 | multi_reader.fill(64, timeout) catch |err| switch (err) { |
| 1927 | 1927 | error.Timeout => return .{ .timeout = .{ |
| 1928 | 1928 | .active_test_index = active_test_index, |
| 1929 | .ns_elapsed = @intCast((try last_update.untilNow(io)).raw.nanoseconds), | |
| 1929 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 1930 | 1930 | } }, |
| 1931 | 1931 | error.EndOfStream => return .{ .no_poll = .{ |
| 1932 | 1932 | .active_test_index = active_test_index, |
| 1933 | .ns_elapsed = @intCast((try last_update.untilNow(io)).raw.nanoseconds), | |
| 1933 | .ns_elapsed = @intCast(last_update.untilNow(io).raw.nanoseconds), | |
| 1934 | 1934 | } }, |
| 1935 | 1935 | else => |e| return e, |
| 1936 | 1936 | }; |
| ... | ... | @@ -1976,13 +1976,13 @@ fn waitZigTest( |
| 1976 | 1976 | @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64)); |
| 1977 | 1977 | |
| 1978 | 1978 | active_test_index = null; |
| 1979 | last_update = try .now(io, .awake); | |
| 1979 | last_update = .now(io, .awake); | |
| 1980 | 1980 | |
| 1981 | 1981 | requestNextTest(io, child.stdin.?, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err }; |
| 1982 | 1982 | }, |
| 1983 | 1983 | .test_started => { |
| 1984 | 1984 | active_test_index = opt_metadata.*.?.next_index - 1; |
| 1985 | last_update = try .now(io, .awake); | |
| 1985 | last_update = .now(io, .awake); | |
| 1986 | 1986 | }, |
| 1987 | 1987 | .test_results => { |
| 1988 | 1988 | assert(fuzz_context == null); |
| ... | ... | @@ -2026,7 +2026,7 @@ fn waitZigTest( |
| 2026 | 2026 | |
| 2027 | 2027 | active_test_index = null; |
| 2028 | 2028 | |
| 2029 | const now: Io.Clock.Timestamp = try .now(io, .awake); | |
| 2029 | const now: Io.Clock.Timestamp = .now(io, .awake); | |
| 2030 | 2030 | md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds); |
| 2031 | 2031 | last_update = now; |
| 2032 | 2032 | |
| ... | ... | @@ -2239,7 +2239,7 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul |
| 2239 | 2239 | return error.StderrStreamTooLong; |
| 2240 | 2240 | } |
| 2241 | 2241 | } else |err| switch (err) { |
| 2242 | error.UnsupportedClock, error.Timeout => unreachable, | |
| 2242 | error.Timeout => unreachable, | |
| 2243 | 2243 | error.EndOfStream => {}, |
| 2244 | 2244 | else => |e| return e, |
| 2245 | 2245 | } |
lib/std/Build/WebServer.zig+3-3| ... | ... | @@ -243,7 +243,7 @@ pub fn finishBuild(ws: *WebServer, opts: struct { |
| 243 | 243 | |
| 244 | 244 | pub fn now(s: *const WebServer) i64 { |
| 245 | 245 | const io = s.graph.io; |
| 246 | const ts = base_clock.now(io) catch s.base_timestamp; | |
| 246 | const ts = base_clock.now(io); | |
| 247 | 247 | return @intCast(s.base_timestamp.durationTo(ts).toNanoseconds()); |
| 248 | 248 | } |
| 249 | 249 | |
| ... | ... | @@ -761,7 +761,7 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct { |
| 761 | 761 | ws.notifyUpdate(); |
| 762 | 762 | } |
| 763 | 763 | |
| 764 | pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64) void { | |
| 764 | pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, duration: Io.Duration) void { | |
| 765 | 765 | const gpa = ws.gpa; |
| 766 | 766 | const io = ws.graph.io; |
| 767 | 767 | |
| ... | ... | @@ -780,7 +780,7 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64) |
| 780 | 780 | const out: *align(1) abi.time_report.GenericResult = @ptrCast(buf); |
| 781 | 781 | out.* = .{ |
| 782 | 782 | .step_idx = step_idx, |
| 783 | .ns_total = ns_total, | |
| 783 | .ns_total = @intCast(duration.toNanoseconds()), | |
| 784 | 784 | }; |
| 785 | 785 | { |
| 786 | 786 | ws.time_report_mutex.lock(io) catch return; |
lib/std/Io.zig+93-34| ... | ... | @@ -231,8 +231,9 @@ pub const VTable = struct { |
| 231 | 231 | |
| 232 | 232 | progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File, |
| 233 | 233 | |
| 234 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, | |
| 235 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, | |
| 234 | now: *const fn (?*anyopaque, Clock) Timestamp, | |
| 235 | clockResolution: *const fn (?*anyopaque, Clock) Clock.ResolutionError!Duration, | |
| 236 | sleep: *const fn (?*anyopaque, Timeout) Cancelable!void, | |
| 236 | 237 | |
| 237 | 238 | random: *const fn (?*anyopaque, buffer: []u8) void, |
| 238 | 239 | randomSecure: *const fn (?*anyopaque, buffer: []u8) RandomSecureError!void, |
| ... | ... | @@ -701,30 +702,53 @@ pub const Clock = enum { |
| 701 | 702 | /// thread. |
| 702 | 703 | cpu_thread, |
| 703 | 704 | |
| 704 | pub const Error = error{UnsupportedClock} || UnexpectedError; | |
| 705 | ||
| 706 | /// This function is not cancelable because first of all it does not block, | |
| 707 | /// but more importantly, the cancelation logic itself may want to check | |
| 708 | /// the time. | |
| 709 | pub fn now(clock: Clock, io: Io) Error!Io.Timestamp { | |
| 705 | /// This function is not cancelable because it does not block. | |
| 706 | /// | |
| 707 | /// Resolution is determined by `resolution` which may be 0 if the | |
| 708 | /// clock is unsupported. | |
| 709 | /// | |
| 710 | /// See also: | |
| 711 | /// * `Clock.Timestamp.now` | |
| 712 | pub fn now(clock: Clock, io: Io) Io.Timestamp { | |
| 710 | 713 | return io.vtable.now(io.userdata, clock); |
| 711 | 714 | } |
| 712 | 715 | |
| 716 | pub const ResolutionError = error{ | |
| 717 | ClockUnavailable, | |
| 718 | Unexpected, | |
| 719 | }; | |
| 720 | ||
| 721 | /// Reveals the granularity of `clock`. May be zero, indicating | |
| 722 | /// unsupported clock. | |
| 723 | pub fn resolution(clock: Clock, io: Io) ResolutionError!Io.Duration { | |
| 724 | return io.vtable.clockResolution(io.userdata, clock); | |
| 725 | } | |
| 726 | ||
| 713 | 727 | pub const Timestamp = struct { |
| 714 | 728 | raw: Io.Timestamp, |
| 715 | 729 | clock: Clock, |
| 716 | 730 | |
| 717 | /// This function is not cancelable because first of all it does not block, | |
| 718 | /// but more importantly, the cancelation logic itself may want to check | |
| 719 | /// the time. | |
| 720 | pub fn now(io: Io, clock: Clock) Error!Clock.Timestamp { | |
| 731 | /// This function is not cancelable because it does not block. | |
| 732 | /// | |
| 733 | /// Resolution is determined by `resolution` which may be 0 if | |
| 734 | /// the clock is unsupported. | |
| 735 | /// | |
| 736 | /// See also: | |
| 737 | /// * `Clock.now` | |
| 738 | pub fn now(io: Io, clock: Clock) Clock.Timestamp { | |
| 721 | 739 | return .{ |
| 722 | .raw = try io.vtable.now(io.userdata, clock), | |
| 740 | .raw = io.vtable.now(io.userdata, clock), | |
| 723 | 741 | .clock = clock, |
| 724 | 742 | }; |
| 725 | 743 | } |
| 726 | 744 | |
| 727 | pub fn wait(t: Clock.Timestamp, io: Io) SleepError!void { | |
| 745 | /// Sleeps until the timestamp arrives. | |
| 746 | /// | |
| 747 | /// See also: | |
| 748 | /// * `Io.sleep` | |
| 749 | /// * `Clock.Duration.sleep` | |
| 750 | /// * `Timeout.sleep` | |
| 751 | pub fn wait(t: Clock.Timestamp, io: Io) Cancelable!void { | |
| 728 | 752 | return io.vtable.sleep(io.userdata, .{ .deadline = t }); |
| 729 | 753 | } |
| 730 | 754 | |
| ... | ... | @@ -752,30 +776,38 @@ pub const Clock = enum { |
| 752 | 776 | }; |
| 753 | 777 | } |
| 754 | 778 | |
| 755 | pub fn fromNow(io: Io, duration: Clock.Duration) Error!Clock.Timestamp { | |
| 779 | /// Resolution is determined by `resolution` which may be 0 if | |
| 780 | /// the clock is unsupported. | |
| 781 | pub fn fromNow(io: Io, duration: Clock.Duration) Clock.Timestamp { | |
| 756 | 782 | return .{ |
| 757 | 783 | .clock = duration.clock, |
| 758 | .raw = (try duration.clock.now(io)).addDuration(duration.raw), | |
| 784 | .raw = duration.clock.now(io).addDuration(duration.raw), | |
| 759 | 785 | }; |
| 760 | 786 | } |
| 761 | 787 | |
| 762 | pub fn untilNow(timestamp: Clock.Timestamp, io: Io) Error!Clock.Duration { | |
| 763 | const now_ts = try Clock.Timestamp.now(io, timestamp.clock); | |
| 788 | /// Resolution is determined by `resolution` which may be 0 if | |
| 789 | /// the clock is unsupported. | |
| 790 | pub fn untilNow(timestamp: Clock.Timestamp, io: Io) Clock.Duration { | |
| 791 | const now_ts = Clock.Timestamp.now(io, timestamp.clock); | |
| 764 | 792 | return timestamp.durationTo(now_ts); |
| 765 | 793 | } |
| 766 | 794 | |
| 767 | pub fn durationFromNow(timestamp: Clock.Timestamp, io: Io) Error!Clock.Duration { | |
| 768 | const now_ts = try timestamp.clock.now(io); | |
| 795 | /// Resolution is determined by `resolution` which may be 0 if | |
| 796 | /// the clock is unsupported. | |
| 797 | pub fn durationFromNow(timestamp: Clock.Timestamp, io: Io) Clock.Duration { | |
| 798 | const now_ts = timestamp.clock.now(io); | |
| 769 | 799 | return .{ |
| 770 | 800 | .clock = timestamp.clock, |
| 771 | 801 | .raw = now_ts.durationTo(timestamp.raw), |
| 772 | 802 | }; |
| 773 | 803 | } |
| 774 | 804 | |
| 775 | pub fn toClock(t: Clock.Timestamp, io: Io, clock: Clock) Error!Clock.Timestamp { | |
| 805 | /// Resolution is determined by `resolution` which may be 0 if | |
| 806 | /// the clock is unsupported. | |
| 807 | pub fn toClock(t: Clock.Timestamp, io: Io, clock: Clock) Clock.Timestamp { | |
| 776 | 808 | if (t.clock == clock) return t; |
| 777 | const now_old = try t.clock.now(io); | |
| 778 | const now_new = try clock.now(io); | |
| 809 | const now_old = t.clock.now(io); | |
| 810 | const now_new = clock.now(io); | |
| 779 | 811 | const duration = now_old.durationTo(t); |
| 780 | 812 | return .{ |
| 781 | 813 | .clock = clock, |
| ... | ... | @@ -793,7 +825,13 @@ pub const Clock = enum { |
| 793 | 825 | raw: Io.Duration, |
| 794 | 826 | clock: Clock, |
| 795 | 827 | |
| 796 | pub fn sleep(duration: Clock.Duration, io: Io) SleepError!void { | |
| 828 | /// Waits until a specified amount of time has passed on `clock`. | |
| 829 | /// | |
| 830 | /// See also: | |
| 831 | /// * `Io.sleep` | |
| 832 | /// * `Clock.Timestamp.wait` | |
| 833 | /// * `Timeout.sleep` | |
| 834 | pub fn sleep(duration: Clock.Duration, io: Io) Cancelable!void { | |
| 797 | 835 | return io.vtable.sleep(io.userdata, .{ .duration = duration }); |
| 798 | 836 | } |
| 799 | 837 | }; |
| ... | ... | @@ -802,6 +840,10 @@ pub const Clock = enum { |
| 802 | 840 | pub const Timestamp = struct { |
| 803 | 841 | nanoseconds: i96, |
| 804 | 842 | |
| 843 | pub fn now(io: Io, clock: Clock) Io.Timestamp { | |
| 844 | return io.vtable.now(io.userdata, clock); | |
| 845 | } | |
| 846 | ||
| 805 | 847 | pub const zero: Timestamp = .{ .nanoseconds = 0 }; |
| 806 | 848 | |
| 807 | 849 | pub fn durationTo(from: Timestamp, to: Timestamp) Duration { |
| ... | ... | @@ -844,6 +886,13 @@ pub const Timestamp = struct { |
| 844 | 886 | .fill = n.fill, |
| 845 | 887 | }); |
| 846 | 888 | } |
| 889 | ||
| 890 | /// Resolution is determined by `Clock.resolution` which may be 0 if | |
| 891 | /// the clock is unsupported. | |
| 892 | pub fn untilNow(t: Timestamp, io: Io, clock: Clock) Duration { | |
| 893 | const now_ts = clock.now(io); | |
| 894 | return t.durationTo(now_ts); | |
| 895 | } | |
| 847 | 896 | }; |
| 848 | 897 | |
| 849 | 898 | pub const Duration = struct { |
| ... | ... | @@ -883,12 +932,12 @@ pub const Timeout = union(enum) { |
| 883 | 932 | duration: Clock.Duration, |
| 884 | 933 | deadline: Clock.Timestamp, |
| 885 | 934 | |
| 886 | pub const Error = error{ Timeout, UnsupportedClock }; | |
| 935 | pub const Error = error{Timeout}; | |
| 887 | 936 | |
| 888 | pub fn toTimestamp(t: Timeout, io: Io) Clock.Error!?Clock.Timestamp { | |
| 937 | pub fn toTimestamp(t: Timeout, io: Io) ?Clock.Timestamp { | |
| 889 | 938 | return switch (t) { |
| 890 | 939 | .none => null, |
| 891 | .duration => |d| try .fromNow(io, d), | |
| 940 | .duration => |d| .fromNow(io, d), | |
| 892 | 941 | .deadline => |d| d, |
| 893 | 942 | }; |
| 894 | 943 | } |
| ... | ... | @@ -896,20 +945,26 @@ pub const Timeout = union(enum) { |
| 896 | 945 | pub fn toDeadline(t: Timeout, io: Io) Timeout { |
| 897 | 946 | return switch (t) { |
| 898 | 947 | .none => .none, |
| 899 | .duration => |d| .{ .deadline = Clock.Timestamp.fromNow(io, d) catch @panic("TODO") }, | |
| 948 | .duration => |d| .{ .deadline = .fromNow(io, d) }, | |
| 900 | 949 | .deadline => |d| .{ .deadline = d }, |
| 901 | 950 | }; |
| 902 | 951 | } |
| 903 | 952 | |
| 904 | pub fn toDurationFromNow(t: Timeout, io: Io) Clock.Error!?Clock.Duration { | |
| 953 | pub fn toDurationFromNow(t: Timeout, io: Io) ?Clock.Duration { | |
| 905 | 954 | return switch (t) { |
| 906 | 955 | .none => null, |
| 907 | 956 | .duration => |d| d, |
| 908 | .deadline => |d| try d.durationFromNow(io), | |
| 957 | .deadline => |d| d.durationFromNow(io), | |
| 909 | 958 | }; |
| 910 | 959 | } |
| 911 | 960 | |
| 912 | pub fn sleep(timeout: Timeout, io: Io) SleepError!void { | |
| 961 | /// Waits until the timeout has passed. | |
| 962 | /// | |
| 963 | /// See also: | |
| 964 | /// * `Io.sleep` | |
| 965 | /// * `Clock.Duration.sleep` | |
| 966 | /// * `Clock.Timestamp.wait` | |
| 967 | pub fn sleep(timeout: Timeout, io: Io) Cancelable!void { | |
| 913 | 968 | return io.vtable.sleep(io.userdata, timeout); |
| 914 | 969 | } |
| 915 | 970 | }; |
| ... | ... | @@ -2027,9 +2082,13 @@ pub fn concurrent( |
| 2027 | 2082 | return future; |
| 2028 | 2083 | } |
| 2029 | 2084 | |
| 2030 | pub const SleepError = error{UnsupportedClock} || UnexpectedError || Cancelable; | |
| 2031 | ||
| 2032 | pub fn sleep(io: Io, duration: Duration, clock: Clock) SleepError!void { | |
| 2085 | /// Waits until a specified amount of time has passed on `clock`. | |
| 2086 | /// | |
| 2087 | /// See also: | |
| 2088 | /// * `Clock.Duration.sleep` | |
| 2089 | /// * `Clock.Timestamp.wait` | |
| 2090 | /// * `Timeout.sleep` | |
| 2091 | pub fn sleep(io: Io, duration: Duration, clock: Clock) Cancelable!void { | |
| 2033 | 2092 | return io.vtable.sleep(io.userdata, .{ .duration = .{ |
| 2034 | 2093 | .raw = duration, |
| 2035 | 2094 | .clock = clock, |
lib/std/Io/File/MultiReader.zig+1-1| ... | ... | @@ -179,7 +179,7 @@ fn rebase(r: *Io.Reader, capacity: usize) Io.Reader.RebaseError!void { |
| 179 | 179 | |
| 180 | 180 | fn fillUntimed(context: *Context, capacity: usize) Io.Reader.Error!void { |
| 181 | 181 | fill(context.mr, capacity, .none) catch |err| switch (err) { |
| 182 | error.Timeout, error.UnsupportedClock => unreachable, | |
| 182 | error.Timeout => unreachable, | |
| 183 | 183 | error.Canceled, error.ConcurrencyUnavailable => |e| { |
| 184 | 184 | context.err = e; |
| 185 | 185 | return error.ReadFailed; |
lib/std/Io/Threaded.zig+99-61| ... | ... | @@ -1712,6 +1712,7 @@ pub fn io(t: *Threaded) Io { |
| 1712 | 1712 | .progressParentFile = progressParentFile, |
| 1713 | 1713 | |
| 1714 | 1714 | .now = now, |
| 1715 | .clockResolution = clockResolution, | |
| 1715 | 1716 | .sleep = sleep, |
| 1716 | 1717 | |
| 1717 | 1718 | .random = random, |
| ... | ... | @@ -1875,6 +1876,7 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1875 | 1876 | .progressParentFile = progressParentFile, |
| 1876 | 1877 | |
| 1877 | 1878 | .now = now, |
| 1879 | .clockResolution = clockResolution, | |
| 1878 | 1880 | .sleep = sleep, |
| 1879 | 1881 | |
| 1880 | 1882 | .random = random, |
| ... | ... | @@ -2487,7 +2489,7 @@ fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io. |
| 2487 | 2489 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2488 | 2490 | const t_io = ioBasic(t); |
| 2489 | 2491 | const timeout_ns: ?u64 = ns: { |
| 2490 | const d = (timeout.toDurationFromNow(t_io) catch break :ns 10) orelse break :ns null; | |
| 2492 | const d = timeout.toDurationFromNow(t_io) orelse break :ns null; | |
| 2491 | 2493 | break :ns std.math.lossyCast(u64, d.raw.toNanoseconds()); |
| 2492 | 2494 | }; |
| 2493 | 2495 | return Thread.futexWait(ptr, expected, timeout_ns); |
| ... | ... | @@ -2655,24 +2657,12 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void { |
| 2655 | 2657 | fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.AwaitConcurrentError!void { |
| 2656 | 2658 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2657 | 2659 | if (is_windows) { |
| 2658 | const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t)) catch |err| switch (err) { | |
| 2659 | error.Unexpected => deadline: { | |
| 2660 | recoverableOsBugDetected(); | |
| 2661 | break :deadline .{ .raw = .{ .nanoseconds = 0 }, .clock = .awake }; | |
| 2662 | }, | |
| 2663 | error.UnsupportedClock => |e| return e, | |
| 2664 | }; | |
| 2660 | const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t)); | |
| 2665 | 2661 | try batchAwaitWindows(b, true); |
| 2666 | 2662 | while (b.pending.head != .none and b.completions.head == .none) { |
| 2667 | 2663 | var delay_interval: windows.LARGE_INTEGER = interval: { |
| 2668 | 2664 | const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER); |
| 2669 | break :interval t.deadlineToWindowsInterval(d) catch |err| switch (err) { | |
| 2670 | error.UnsupportedClock => |e| return e, | |
| 2671 | error.Unexpected => { | |
| 2672 | recoverableOsBugDetected(); | |
| 2673 | break :interval -1; | |
| 2674 | }, | |
| 2675 | }; | |
| 2665 | break :interval t.deadlineToWindowsInterval(d); | |
| 2676 | 2666 | }; |
| 2677 | 2667 | const alertable_syscall = try AlertableSyscall.start(); |
| 2678 | 2668 | const delay_rc = windows.ntdll.NtDelayExecution(windows.TRUE, &delay_interval); |
| ... | ... | @@ -2754,7 +2744,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2754 | 2744 | else => {}, |
| 2755 | 2745 | } |
| 2756 | 2746 | const t_io = ioBasic(t); |
| 2757 | const deadline = timeout.toTimestamp(t_io) catch return error.UnsupportedClock; | |
| 2747 | const deadline = timeout.toTimestamp(t_io); | |
| 2758 | 2748 | while (true) { |
| 2759 | 2749 | const timeout_ms: i32 = t: { |
| 2760 | 2750 | if (b.completions.head != .none) { |
| ... | ... | @@ -2765,7 +2755,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout |
| 2765 | 2755 | break :t 0; |
| 2766 | 2756 | } |
| 2767 | 2757 | const d = deadline orelse break :t -1; |
| 2768 | const duration = d.durationFromNow(t_io) catch return error.UnsupportedClock; | |
| 2758 | const duration = d.durationFromNow(t_io); | |
| 2769 | 2759 | if (duration.raw.nanoseconds <= 0) return error.Timeout; |
| 2770 | 2760 | const max_poll_ms = std.math.maxInt(i32); |
| 2771 | 2761 | break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds())); |
| ... | ... | @@ -10821,22 +10811,21 @@ fn fileWriteFilePositional( |
| 10821 | 10811 | return error.Unimplemented; |
| 10822 | 10812 | } |
| 10823 | 10813 | |
| 10824 | fn nowPosix(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | |
| 10814 | fn nowPosix(clock: Io.Clock) Io.Timestamp { | |
| 10825 | 10815 | const clock_id: posix.clockid_t = clockToPosix(clock); |
| 10826 | var tp: posix.timespec = undefined; | |
| 10827 | switch (posix.errno(posix.system.clock_gettime(clock_id, &tp))) { | |
| 10828 | .SUCCESS => return timestampFromPosix(&tp), | |
| 10829 | .INVAL => return error.UnsupportedClock, | |
| 10830 | else => |err| return posix.unexpectedErrno(err), | |
| 10816 | var timespec: posix.timespec = undefined; | |
| 10817 | switch (posix.errno(posix.system.clock_gettime(clock_id, &timespec))) { | |
| 10818 | .SUCCESS => return timestampFromPosix(&timespec), | |
| 10819 | else => return .zero, | |
| 10831 | 10820 | } |
| 10832 | 10821 | } |
| 10833 | 10822 | |
| 10834 | fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | |
| 10823 | fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Timestamp { | |
| 10835 | 10824 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 10836 | 10825 | _ = t; |
| 10837 | 10826 | return nowInner(clock); |
| 10838 | 10827 | } |
| 10839 | fn nowInner(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | |
| 10828 | fn nowInner(clock: Io.Clock) Io.Timestamp { | |
| 10840 | 10829 | return switch (native_os) { |
| 10841 | 10830 | .windows => nowWindows(clock), |
| 10842 | 10831 | .wasi => nowWasi(clock), |
| ... | ... | @@ -10844,7 +10833,55 @@ fn nowInner(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 10844 | 10833 | }; |
| 10845 | 10834 | } |
| 10846 | 10835 | |
| 10847 | fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | |
| 10836 | fn clockResolution(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.ResolutionError!Io.Duration { | |
| 10837 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | |
| 10838 | _ = t; | |
| 10839 | return switch (native_os) { | |
| 10840 | .windows => switch (clock) { | |
| 10841 | .awake, .boot, .real => { | |
| 10842 | // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA | |
| 10843 | // (a read-only page of info updated and mapped by the kernel to all processes): | |
| 10844 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data | |
| 10845 | // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm | |
| 10846 | var qpf: windows.LARGE_INTEGER = undefined; | |
| 10847 | if (windows.ntdll.RtlQueryPerformanceFrequency(&qpf) != 0) { | |
| 10848 | recoverableOsBugDetected(); | |
| 10849 | return .zero; | |
| 10850 | } | |
| 10851 | // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it. | |
| 10852 | // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701 | |
| 10853 | const common_qpf = 10_000_000; | |
| 10854 | if (qpf == common_qpf) return .fromNanoseconds(std.time.ns_per_s / common_qpf); | |
| 10855 | ||
| 10856 | // Convert to ns using fixed point. | |
| 10857 | const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf)); | |
| 10858 | const result = scale >> 32; | |
| 10859 | return .fromNanoseconds(result); | |
| 10860 | }, | |
| 10861 | .cpu_process, .cpu_thread => return .zero, | |
| 10862 | }, | |
| 10863 | .wasi => { | |
| 10864 | if (builtin.link_libc) return clockResolutionPosix(clock); | |
| 10865 | var ns: std.os.wasi.timestamp_t = undefined; | |
| 10866 | return switch (std.os.wasi.clock_res_get(clockToWasi(clock), &ns)) { | |
| 10867 | .SUCCESS => .fromNanoseconds(ns), | |
| 10868 | else => .zero, | |
| 10869 | }; | |
| 10870 | }, | |
| 10871 | else => return clockResolutionPosix(clock), | |
| 10872 | }; | |
| 10873 | } | |
| 10874 | ||
| 10875 | fn clockResolutionPosix(clock: Io.Clock) Io.Clock.ResolutionError!Io.Duration { | |
| 10876 | const clock_id: posix.clockid_t = clockToPosix(clock); | |
| 10877 | var timespec: posix.timespec = undefined; | |
| 10878 | return switch (posix.errno(posix.system.clock_getres(clock_id, &timespec))) { | |
| 10879 | .SUCCESS => .fromNanoseconds(nanosecondsFromPosix(&timespec)), | |
| 10880 | else => .zero, | |
| 10881 | }; | |
| 10882 | } | |
| 10883 | ||
| 10884 | fn nowWindows(clock: Io.Clock) Io.Timestamp { | |
| 10848 | 10885 | switch (clock) { |
| 10849 | 10886 | .real => { |
| 10850 | 10887 | // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds |
| ... | ... | @@ -10882,8 +10919,7 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 10882 | 10919 | &times, |
| 10883 | 10920 | @sizeOf(windows.KERNEL_USER_TIMES), |
| 10884 | 10921 | null, |
| 10885 | ) != .SUCCESS) | |
| 10886 | return error.Unexpected; | |
| 10922 | ) != .SUCCESS) return .zero; | |
| 10887 | 10923 | |
| 10888 | 10924 | const sum = @as(i96, times.UserTime) + @as(i96, times.KernelTime); |
| 10889 | 10925 | return .{ .nanoseconds = sum * 100 }; |
| ... | ... | @@ -10899,8 +10935,7 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 10899 | 10935 | &times, |
| 10900 | 10936 | @sizeOf(windows.KERNEL_USER_TIMES), |
| 10901 | 10937 | null, |
| 10902 | ) != .SUCCESS) | |
| 10903 | return error.Unexpected; | |
| 10938 | ) != .SUCCESS) return .zero; | |
| 10904 | 10939 | |
| 10905 | 10940 | const sum = @as(i96, times.UserTime) + @as(i96, times.KernelTime); |
| 10906 | 10941 | return .{ .nanoseconds = sum * 100 }; |
| ... | ... | @@ -10908,23 +10943,23 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 10908 | 10943 | } |
| 10909 | 10944 | } |
| 10910 | 10945 | |
| 10911 | fn nowWasi(clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | |
| 10946 | fn nowWasi(clock: Io.Clock) Io.Timestamp { | |
| 10912 | 10947 | var ns: std.os.wasi.timestamp_t = undefined; |
| 10913 | 10948 | const err = std.os.wasi.clock_time_get(clockToWasi(clock), 1, &ns); |
| 10914 | if (err != .SUCCESS) return error.Unexpected; | |
| 10949 | if (err != .SUCCESS) return .zero; | |
| 10915 | 10950 | return .fromNanoseconds(ns); |
| 10916 | 10951 | } |
| 10917 | 10952 | |
| 10918 | fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { | |
| 10953 | fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.Cancelable!void { | |
| 10919 | 10954 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 10920 | 10955 | if (timeout == .none) return; |
| 10921 | if (use_parking_sleep) return parking_sleep.sleep(try timeout.toTimestamp(ioBasic(t))); | |
| 10956 | if (use_parking_sleep) return parking_sleep.sleep(timeout.toTimestamp(ioBasic(t))); | |
| 10922 | 10957 | if (native_os == .wasi) return sleepWasi(t, timeout); |
| 10923 | 10958 | if (@TypeOf(posix.system.clock_nanosleep) != void) return sleepPosix(timeout); |
| 10924 | 10959 | return sleepNanosleep(t, timeout); |
| 10925 | 10960 | } |
| 10926 | 10961 | |
| 10927 | fn sleepPosix(timeout: Io.Timeout) Io.SleepError!void { | |
| 10962 | fn sleepPosix(timeout: Io.Timeout) Io.Cancelable!void { | |
| 10928 | 10963 | const clock_id: posix.clockid_t = clockToPosix(switch (timeout) { |
| 10929 | 10964 | .none => .awake, |
| 10930 | 10965 | .duration => |d| d.clock, |
| ... | ... | @@ -10944,25 +10979,27 @@ fn sleepPosix(timeout: Io.Timeout) Io.SleepError!void { |
| 10944 | 10979 | } }, &timespec, &timespec); |
| 10945 | 10980 | // POSIX-standard libc clock_nanosleep() returns *positive* errno values directly |
| 10946 | 10981 | switch (if (builtin.link_libc) @as(posix.E, @enumFromInt(rc)) else posix.errno(rc)) { |
| 10947 | .SUCCESS => { | |
| 10948 | syscall.finish(); | |
| 10949 | return; | |
| 10950 | }, | |
| 10951 | 10982 | .INTR => { |
| 10952 | 10983 | try syscall.checkCancel(); |
| 10953 | 10984 | continue; |
| 10954 | 10985 | }, |
| 10955 | .INVAL => return syscall.fail(error.UnsupportedClock), | |
| 10956 | else => |err| return syscall.unexpectedErrno(err), | |
| 10986 | // Handles SUCCESS as well as clock not available and unexpected | |
| 10987 | // errors. The user had a chance to check clock resolution before | |
| 10988 | // getting here, which would have reported 0, making this a legal | |
| 10989 | // amount of time to sleep. | |
| 10990 | else => { | |
| 10991 | syscall.finish(); | |
| 10992 | return; | |
| 10993 | }, | |
| 10957 | 10994 | } |
| 10958 | 10995 | } |
| 10959 | 10996 | } |
| 10960 | 10997 | |
| 10961 | fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void { | |
| 10998 | fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void { | |
| 10962 | 10999 | const t_io = ioBasic(t); |
| 10963 | 11000 | const w = std.os.wasi; |
| 10964 | 11001 | |
| 10965 | const clock: w.subscription_clock_t = if (try timeout.toDurationFromNow(t_io)) |d| .{ | |
| 11002 | const clock: w.subscription_clock_t = if (timeout.toDurationFromNow(t_io)) |d| .{ | |
| 10966 | 11003 | .id = clockToWasi(d.clock), |
| 10967 | 11004 | .timeout = std.math.lossyCast(u64, d.raw.nanoseconds), |
| 10968 | 11005 | .precision = 0, |
| ... | ... | @@ -10987,13 +11024,13 @@ fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void { |
| 10987 | 11024 | syscall.finish(); |
| 10988 | 11025 | } |
| 10989 | 11026 | |
| 10990 | fn sleepNanosleep(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void { | |
| 11027 | fn sleepNanosleep(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void { | |
| 10991 | 11028 | const t_io = ioBasic(t); |
| 10992 | 11029 | const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type; |
| 10993 | 11030 | const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type; |
| 10994 | 11031 | |
| 10995 | 11032 | var timespec: posix.timespec = t: { |
| 10996 | const d = (try timeout.toDurationFromNow(t_io)) orelse break :t .{ | |
| 11033 | const d = timeout.toDurationFromNow(t_io) orelse break :t .{ | |
| 10997 | 11034 | .sec = std.math.maxInt(sec_type), |
| 10998 | 11035 | .nsec = std.math.maxInt(nsec_type), |
| 10999 | 11036 | }; |
| ... | ... | @@ -12630,7 +12667,7 @@ fn netReceivePosix( |
| 12630 | 12667 | var message_i: usize = 0; |
| 12631 | 12668 | var data_i: usize = 0; |
| 12632 | 12669 | |
| 12633 | const deadline = timeout.toTimestamp(t_io) catch |err| return .{ err, message_i }; | |
| 12670 | const deadline = timeout.toTimestamp(t_io); | |
| 12634 | 12671 | |
| 12635 | 12672 | recv: while (true) { |
| 12636 | 12673 | if (message_buffer.len - message_i == 0) return .{ null, message_i }; |
| ... | ... | @@ -12678,7 +12715,7 @@ fn netReceivePosix( |
| 12678 | 12715 | |
| 12679 | 12716 | const max_poll_ms = std.math.maxInt(u31); |
| 12680 | 12717 | const timeout_ms: u31 = if (deadline) |d| t: { |
| 12681 | const duration = d.durationFromNow(t_io) catch |err| return .{ err, message_i }; | |
| 12718 | const duration = d.durationFromNow(t_io); | |
| 12682 | 12719 | if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i }; |
| 12683 | 12720 | break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds())); |
| 12684 | 12721 | } else max_poll_ms; |
| ... | ... | @@ -13875,7 +13912,11 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) File.Stat { |
| 13875 | 13912 | } |
| 13876 | 13913 | |
| 13877 | 13914 | fn timestampFromPosix(timespec: *const posix.timespec) Io.Timestamp { |
| 13878 | return .{ .nanoseconds = @intCast(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec) }; | |
| 13915 | return .{ .nanoseconds = nanosecondsFromPosix(timespec) }; | |
| 13916 | } | |
| 13917 | ||
| 13918 | fn nanosecondsFromPosix(timespec: *const posix.timespec) i96 { | |
| 13919 | return @intCast(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec); | |
| 13879 | 13920 | } |
| 13880 | 13921 | |
| 13881 | 13922 | fn timestampToPosix(nanoseconds: i96) posix.timespec { |
| ... | ... | @@ -14013,13 +14054,13 @@ fn lookupDns( |
| 14013 | 14054 | // boot clock is chosen because time the computer is suspended should count |
| 14014 | 14055 | // against time spent waiting for external messages to arrive. |
| 14015 | 14056 | const clock: Io.Clock = .boot; |
| 14016 | var now_ts = try clock.now(t_io); | |
| 14057 | var now_ts = clock.now(t_io); | |
| 14017 | 14058 | const final_ts = now_ts.addDuration(.fromSeconds(rc.timeout_seconds)); |
| 14018 | 14059 | const attempt_duration: Io.Duration = .{ |
| 14019 | 14060 | .nanoseconds = (std.time.ns_per_s / rc.attempts) * @as(i96, rc.timeout_seconds), |
| 14020 | 14061 | }; |
| 14021 | 14062 | |
| 14022 | send: while (now_ts.nanoseconds < final_ts.nanoseconds) : (now_ts = try clock.now(t_io)) { | |
| 14063 | send: while (now_ts.nanoseconds < final_ts.nanoseconds) : (now_ts = clock.now(t_io)) { | |
| 14023 | 14064 | const max_messages = queries_buffer.len * HostName.ResolvConf.max_nameservers; |
| 14024 | 14065 | { |
| 14025 | 14066 | var message_buffer: [max_messages]Io.net.OutgoingMessage = undefined; |
| ... | ... | @@ -17021,7 +17062,7 @@ const parking_futex = struct { |
| 17021 | 17062 | const deadline: ?Io.Clock.Timestamp = switch (timeout) { |
| 17022 | 17063 | .none => null, |
| 17023 | 17064 | .duration => |d| .{ |
| 17024 | .raw = (nowInner(d.clock) catch unreachable).addDuration(d.raw), | |
| 17065 | .raw = nowInner(d.clock).addDuration(d.raw), | |
| 17025 | 17066 | .clock = d.clock, |
| 17026 | 17067 | }, |
| 17027 | 17068 | .deadline => |d| d, |
| ... | ... | @@ -17143,7 +17184,7 @@ const parking_sleep = struct { |
| 17143 | 17184 | comptime { |
| 17144 | 17185 | assert(use_parking_sleep); |
| 17145 | 17186 | } |
| 17146 | fn sleep(deadline: ?Io.Clock.Timestamp) Io.SleepError!void { | |
| 17187 | fn sleep(deadline: ?Io.Clock.Timestamp) Io.Cancelable!void { | |
| 17147 | 17188 | const opt_thread = Thread.current; |
| 17148 | 17189 | cancelable: { |
| 17149 | 17190 | const thread = opt_thread orelse break :cancelable; |
| ... | ... | @@ -17216,12 +17257,9 @@ const parking_sleep = struct { |
| 17216 | 17257 | } |
| 17217 | 17258 | /// Sleep for approximately `ms` awake milliseconds in an attempt to work around Windows kernel bugs. |
| 17218 | 17259 | fn windowsRetrySleep(ms: u32) (Io.Cancelable || Io.UnexpectedError)!void { |
| 17219 | const now_timestamp = nowWindows(.awake) catch unreachable; // '.awake' is supported on Windows | |
| 17260 | const now_timestamp = nowWindows(.awake); // '.awake' is supported on Windows | |
| 17220 | 17261 | const deadline = now_timestamp.addDuration(.fromMilliseconds(ms)); |
| 17221 | parking_sleep.sleep(.{ .raw = deadline, .clock = .awake }) catch |err| switch (err) { | |
| 17222 | error.UnsupportedClock => unreachable, | |
| 17223 | else => |e| return e, | |
| 17224 | }; | |
| 17262 | try parking_sleep.sleep(.{ .raw = deadline, .clock = .awake }); | |
| 17225 | 17263 | } |
| 17226 | 17264 | }; |
| 17227 | 17265 | |
| ... | ... | @@ -17234,7 +17272,7 @@ fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{T |
| 17234 | 17272 | .windows => { |
| 17235 | 17273 | var timeout_buf: windows.LARGE_INTEGER = undefined; |
| 17236 | 17274 | const raw_timeout: ?*windows.LARGE_INTEGER = if (opt_deadline) |deadline| timeout: { |
| 17237 | const now_timestamp = nowWindows(deadline.clock) catch unreachable; | |
| 17275 | const now_timestamp = nowWindows(deadline.clock); | |
| 17238 | 17276 | const nanoseconds = now_timestamp.durationTo(deadline.raw).nanoseconds; |
| 17239 | 17277 | timeout_buf = @intCast(@divTrunc(-nanoseconds, 100)); |
| 17240 | 17278 | break :timeout &timeout_buf; |
| ... | ... | @@ -17284,17 +17322,17 @@ fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{T |
| 17284 | 17322 | } |
| 17285 | 17323 | } |
| 17286 | 17324 | |
| 17287 | fn deadlineToWindowsInterval(t: *Io.Threaded, deadline: Io.Clock.Timestamp) Io.Clock.Error!windows.LARGE_INTEGER { | |
| 17325 | fn deadlineToWindowsInterval(t: *Io.Threaded, deadline: Io.Clock.Timestamp) windows.LARGE_INTEGER { | |
| 17288 | 17326 | // ntdll only supports two combinations: |
| 17289 | 17327 | // * real-time (`.real`) sleeps with absolute deadlines |
| 17290 | 17328 | // * monotonic (`.awake`/`.boot`) sleeps with relative durations |
| 17291 | 17329 | switch (deadline.clock) { |
| 17292 | .cpu_process, .cpu_thread => unreachable, // cannot sleep for CPU time | |
| 17330 | .cpu_process, .cpu_thread => return 0, | |
| 17293 | 17331 | .real => { |
| 17294 | 17332 | return @intCast(@max(@divTrunc(deadline.raw.nanoseconds, 100), 0)); |
| 17295 | 17333 | }, |
| 17296 | 17334 | .awake, .boot => { |
| 17297 | const duration = try deadline.durationFromNow(ioBasic(t)); | |
| 17335 | const duration = deadline.durationFromNow(ioBasic(t)); | |
| 17298 | 17336 | return @intCast(@min(@divTrunc(-duration.raw.nanoseconds, 100), -1)); |
| 17299 | 17337 | }, |
| 17300 | 17338 | } |
lib/std/Io/net.zig+1-1| ... | ... | @@ -1137,7 +1137,7 @@ pub const Socket = struct { |
| 1137 | 1137 | const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, .none); |
| 1138 | 1138 | if (maybe_err) |err| switch (err) { |
| 1139 | 1139 | // No timeout is passed to `netReceieve`, so it must not return timeout related errors. |
| 1140 | error.Timeout, error.UnsupportedClock => unreachable, | |
| 1140 | error.Timeout => unreachable, | |
| 1141 | 1141 | else => |e| return e, |
| 1142 | 1142 | }; |
| 1143 | 1143 | assert(1 == count); |
lib/std/Io/net/HostName.zig+1-1| ... | ... | @@ -145,7 +145,7 @@ pub const LookupError = error{ |
| 145 | 145 | NoAddressReturned, |
| 146 | 146 | /// Failed to open or read "/etc/hosts" or "/etc/resolv.conf". |
| 147 | 147 | DetectingNetworkConfigurationFailed, |
| 148 | } || Io.Clock.Error || IpAddress.BindError || Io.Cancelable; | |
| 148 | } || IpAddress.BindError || Io.Cancelable; | |
| 149 | 149 | |
| 150 | 150 | pub const LookupResult = union(enum) { |
| 151 | 151 | address: IpAddress, |
lib/std/Io/test.zig-6| ... | ... | @@ -216,14 +216,12 @@ test "Group.cancel" { |
| 216 | 216 | defer result.* = 1; |
| 217 | 217 | io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) { |
| 218 | 218 | error.Canceled => |e| return e, |
| 219 | else => {}, | |
| 220 | 219 | }; |
| 221 | 220 | } |
| 222 | 221 | |
| 223 | 222 | fn sleepRecancel(io: Io, result: *usize) void { |
| 224 | 223 | io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) { |
| 225 | 224 | error.Canceled => io.recancel(), |
| 226 | else => {}, | |
| 227 | 225 | }; |
| 228 | 226 | result.* = 1; |
| 229 | 227 | } |
| ... | ... | @@ -523,8 +521,6 @@ test "cancel sleep" { |
| 523 | 521 | fn blockUntilCanceled(io: Io) void { |
| 524 | 522 | while (true) io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) { |
| 525 | 523 | error.Canceled => return, |
| 526 | error.UnsupportedClock => @panic("unsupported clock"), | |
| 527 | error.Unexpected => @panic("unexpected"), | |
| 528 | 524 | }; |
| 529 | 525 | } |
| 530 | 526 | }; |
| ... | ... | @@ -552,8 +548,6 @@ test "tasks spawned in group after Group.cancel are canceled" { |
| 552 | 548 | fn blockUntilCanceled(io: Io) Io.Cancelable!void { |
| 553 | 549 | while (true) io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) { |
| 554 | 550 | error.Canceled => |e| return e, |
| 555 | error.UnsupportedClock => @panic("unsupported clock"), | |
| 556 | error.Unexpected => @panic("unexpected"), | |
| 557 | 551 | }; |
| 558 | 552 | } |
| 559 | 553 | }; |
lib/std/crypto/Certificate/Bundle.zig+2-2| ... | ... | @@ -212,7 +212,7 @@ pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp, i |
| 212 | 212 | } |
| 213 | 213 | } |
| 214 | 214 | |
| 215 | pub const AddCertsFromFilePathError = Io.File.OpenError || AddCertsFromFileError || Io.Clock.Error; | |
| 215 | pub const AddCertsFromFilePathError = Io.File.OpenError || AddCertsFromFileError; | |
| 216 | 216 | |
| 217 | 217 | pub fn addCertsFromFilePathAbsolute( |
| 218 | 218 | cb: *Bundle, |
| ... | ... | @@ -338,7 +338,7 @@ test "scan for OS-provided certificates" { |
| 338 | 338 | var bundle: Bundle = .{}; |
| 339 | 339 | defer bundle.deinit(gpa); |
| 340 | 340 | |
| 341 | const now = try Io.Clock.real.now(io); | |
| 341 | const now = Io.Clock.real.now(io); | |
| 342 | 342 | |
| 343 | 343 | try bundle.rescan(gpa, io, now); |
| 344 | 344 | } |
lib/std/http/Client.zig+1-1| ... | ... | @@ -1700,7 +1700,7 @@ pub fn request( |
| 1700 | 1700 | defer client.ca_bundle_mutex.unlock(io); |
| 1701 | 1701 | |
| 1702 | 1702 | if (client.now == null) { |
| 1703 | const now = try Io.Clock.real.now(io); | |
| 1703 | const now = Io.Clock.real.now(io); | |
| 1704 | 1704 | client.now = now; |
| 1705 | 1705 | client.ca_bundle.rescan(client.allocator, io, now) catch |
| 1706 | 1706 | return error.CertificateBundleLoadFailure; |
lib/std/os/linux.zig+5-5| ... | ... | @@ -1914,21 +1914,21 @@ fn init_vdso_clock_gettime(clk: clockid_t, ts: *timespec) callconv(.c) usize { |
| 1914 | 1914 | @atomicStore(?VdsoClockGettime, &vdso_clock_gettime, ptr, .monotonic); |
| 1915 | 1915 | // Call into the VDSO if available |
| 1916 | 1916 | if (ptr) |f| return f(clk, ts); |
| 1917 | return @as(usize, @bitCast(-@as(isize, @intFromEnum(E.NOSYS)))); | |
| 1917 | return @bitCast(-@as(isize, @intFromEnum(E.NOSYS))); | |
| 1918 | 1918 | } |
| 1919 | 1919 | |
| 1920 | pub fn clock_getres(clk_id: i32, tp: *timespec) usize { | |
| 1920 | pub fn clock_getres(clk_id: clockid_t, tp: *timespec) usize { | |
| 1921 | 1921 | return syscall2( |
| 1922 | 1922 | if (@hasField(SYS, "clock_getres") and native_arch != .hexagon) .clock_getres else .clock_getres_time64, |
| 1923 | @as(usize, @bitCast(@as(isize, clk_id))), | |
| 1923 | @as(usize, @intFromEnum(clk_id)), | |
| 1924 | 1924 | @intFromPtr(tp), |
| 1925 | 1925 | ); |
| 1926 | 1926 | } |
| 1927 | 1927 | |
| 1928 | pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { | |
| 1928 | pub fn clock_settime(clk_id: clockid_t, tp: *const timespec) usize { | |
| 1929 | 1929 | return syscall2( |
| 1930 | 1930 | if (@hasField(SYS, "clock_settime") and native_arch != .hexagon) .clock_settime else .clock_settime64, |
| 1931 | @as(usize, @bitCast(@as(isize, clk_id))), | |
| 1931 | @as(usize, @intFromEnum(clk_id)), | |
| 1932 | 1932 | @intFromPtr(tp), |
| 1933 | 1933 | ); |
| 1934 | 1934 | } |
lib/std/os/linux/IoUring/test.zig+2-2| ... | ... | @@ -620,12 +620,12 @@ test "timeout (after a relative time)" { |
| 620 | 620 | const margin = 5; |
| 621 | 621 | const ts: linux.kernel_timespec = .{ .sec = 0, .nsec = ms * 1000000 }; |
| 622 | 622 | |
| 623 | const started = try std.Io.Clock.awake.now(io); | |
| 623 | const started = std.Io.Clock.awake.now(io); | |
| 624 | 624 | const sqe = try ring.timeout(0x55555555, &ts, 0, 0); |
| 625 | 625 | try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe.opcode); |
| 626 | 626 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 627 | 627 | const cqe = try ring.copy_cqe(); |
| 628 | const stopped = try std.Io.Clock.awake.now(io); | |
| 628 | const stopped = std.Io.Clock.awake.now(io); | |
| 629 | 629 | |
| 630 | 630 | try testing.expectEqual(linux.io_uring_cqe{ |
| 631 | 631 | .user_data = 0x55555555, |
lib/std/posix.zig-52| ... | ... | @@ -864,58 +864,6 @@ pub fn dl_iterate_phdr( |
| 864 | 864 | } |
| 865 | 865 | } |
| 866 | 866 | |
| 867 | pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; | |
| 868 | ||
| 869 | pub fn clock_gettime(clock_id: clockid_t) ClockGetTimeError!timespec { | |
| 870 | var tp: timespec = undefined; | |
| 871 | ||
| 872 | if (native_os == .windows) { | |
| 873 | @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.time API"); | |
| 874 | } else if (native_os == .wasi and !builtin.link_libc) { | |
| 875 | var ts: timestamp_t = undefined; | |
| 876 | switch (system.clock_time_get(clock_id, 1, &ts)) { | |
| 877 | .SUCCESS => { | |
| 878 | tp = .{ | |
| 879 | .sec = @intCast(ts / std.time.ns_per_s), | |
| 880 | .nsec = @intCast(ts % std.time.ns_per_s), | |
| 881 | }; | |
| 882 | }, | |
| 883 | .INVAL => return error.UnsupportedClock, | |
| 884 | else => |err| return unexpectedErrno(err), | |
| 885 | } | |
| 886 | return tp; | |
| 887 | } | |
| 888 | ||
| 889 | switch (errno(system.clock_gettime(clock_id, &tp))) { | |
| 890 | .SUCCESS => return tp, | |
| 891 | .FAULT => unreachable, | |
| 892 | .INVAL => return error.UnsupportedClock, | |
| 893 | else => |err| return unexpectedErrno(err), | |
| 894 | } | |
| 895 | } | |
| 896 | ||
| 897 | pub fn clock_getres(clock_id: clockid_t, res: *timespec) ClockGetTimeError!void { | |
| 898 | if (native_os == .wasi and !builtin.link_libc) { | |
| 899 | var ts: timestamp_t = undefined; | |
| 900 | switch (system.clock_res_get(@bitCast(clock_id), &ts)) { | |
| 901 | .SUCCESS => res.* = .{ | |
| 902 | .sec = @intCast(ts / std.time.ns_per_s), | |
| 903 | .nsec = @intCast(ts % std.time.ns_per_s), | |
| 904 | }, | |
| 905 | .INVAL => return error.UnsupportedClock, | |
| 906 | else => |err| return unexpectedErrno(err), | |
| 907 | } | |
| 908 | return; | |
| 909 | } | |
| 910 | ||
| 911 | switch (errno(system.clock_getres(clock_id, res))) { | |
| 912 | .SUCCESS => return, | |
| 913 | .FAULT => unreachable, | |
| 914 | .INVAL => return error.UnsupportedClock, | |
| 915 | else => |err| return unexpectedErrno(err), | |
| 916 | } | |
| 917 | } | |
| 918 | ||
| 919 | 867 | pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError; |
| 920 | 868 | |
| 921 | 869 | pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { |
lib/std/time.zig-182| ... | ... | @@ -1,11 +1,3 @@ |
| 1 | const std = @import("std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const assert = std.debug.assert; | |
| 4 | const testing = std.testing; | |
| 5 | const math = std.math; | |
| 6 | const windows = std.os.windows; | |
| 7 | const posix = std.posix; | |
| 8 | ||
| 9 | 1 | pub const epoch = @import("time/epoch.zig"); |
| 10 | 2 | |
| 11 | 3 | // Divisions of a nanosecond. |
| ... | ... | @@ -38,180 +30,6 @@ pub const s_per_hour = s_per_min * 60; |
| 38 | 30 | pub const s_per_day = s_per_hour * 24; |
| 39 | 31 | pub const s_per_week = s_per_day * 7; |
| 40 | 32 | |
| 41 | /// An Instant represents a timestamp with respect to the currently | |
| 42 | /// executing program that ticks during suspend and can be used to | |
| 43 | /// record elapsed time unlike `nanoTimestamp`. | |
| 44 | /// | |
| 45 | /// It tries to sample the system's fastest and most precise timer available. | |
| 46 | /// It also tries to be monotonic, but this is not a guarantee due to OS/hardware bugs. | |
| 47 | /// If you need monotonic readings for elapsed time, consider `Timer` instead. | |
| 48 | pub const Instant = struct { | |
| 49 | timestamp: if (is_posix) posix.timespec else u64, | |
| 50 | ||
| 51 | // true if we should use clock_gettime() | |
| 52 | const is_posix = switch (builtin.os.tag) { | |
| 53 | .windows, .uefi, .wasi => false, | |
| 54 | else => true, | |
| 55 | }; | |
| 56 | ||
| 57 | /// Queries the system for the current moment of time as an Instant. | |
| 58 | /// This is not guaranteed to be monotonic or steadily increasing, but for | |
| 59 | /// most implementations it is. | |
| 60 | /// Returns `error.Unsupported` when a suitable clock is not detected. | |
| 61 | pub fn now() error{Unsupported}!Instant { | |
| 62 | const clock_id = switch (builtin.os.tag) { | |
| 63 | .windows => { | |
| 64 | // QPC on windows doesn't fail on >= XP/2000 and includes time suspended. | |
| 65 | return .{ .timestamp = windows.QueryPerformanceCounter() }; | |
| 66 | }, | |
| 67 | .wasi => { | |
| 68 | var ns: std.os.wasi.timestamp_t = undefined; | |
| 69 | const rc = std.os.wasi.clock_time_get(.MONOTONIC, 1, &ns); | |
| 70 | if (rc != .SUCCESS) return error.Unsupported; | |
| 71 | return .{ .timestamp = ns }; | |
| 72 | }, | |
| 73 | .uefi => { | |
| 74 | const value, _ = std.os.uefi.system_table.runtime_services.getTime() catch return error.Unsupported; | |
| 75 | return .{ .timestamp = value.toEpoch() }; | |
| 76 | }, | |
| 77 | // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while | |
| 78 | // suspended. | |
| 79 | .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => posix.CLOCK.UPTIME_RAW, | |
| 80 | // On freebsd derivatives, use MONOTONIC_FAST as currently there's | |
| 81 | // no precision tradeoff. | |
| 82 | .freebsd, .dragonfly => posix.CLOCK.MONOTONIC_FAST, | |
| 83 | // On linux, use BOOTTIME instead of MONOTONIC as it ticks while | |
| 84 | // suspended. | |
| 85 | .linux => posix.CLOCK.BOOTTIME, | |
| 86 | // On other posix systems, MONOTONIC is generally the fastest and | |
| 87 | // ticks while suspended. | |
| 88 | else => posix.CLOCK.MONOTONIC, | |
| 89 | }; | |
| 90 | ||
| 91 | const ts = posix.clock_gettime(clock_id) catch return error.Unsupported; | |
| 92 | return .{ .timestamp = ts }; | |
| 93 | } | |
| 94 | ||
| 95 | /// Quickly compares two instances between each other. | |
| 96 | pub fn order(self: Instant, other: Instant) std.math.Order { | |
| 97 | // windows and wasi timestamps are in u64 which is easily comparible | |
| 98 | if (!is_posix) { | |
| 99 | return std.math.order(self.timestamp, other.timestamp); | |
| 100 | } | |
| 101 | ||
| 102 | var ord = std.math.order(self.timestamp.sec, other.timestamp.sec); | |
| 103 | if (ord == .eq) { | |
| 104 | ord = std.math.order(self.timestamp.nsec, other.timestamp.nsec); | |
| 105 | } | |
| 106 | return ord; | |
| 107 | } | |
| 108 | ||
| 109 | /// Returns elapsed time in nanoseconds since the `earlier` Instant. | |
| 110 | /// This assumes that the `earlier` Instant represents a moment in time before or equal to `self`. | |
| 111 | /// This also assumes that the time that has passed between both Instants fits inside a u64 (~585 yrs). | |
| 112 | pub fn since(self: Instant, earlier: Instant) u64 { | |
| 113 | switch (builtin.os.tag) { | |
| 114 | .windows => { | |
| 115 | // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA | |
| 116 | // (a read-only page of info updated and mapped by the kernel to all processes): | |
| 117 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data | |
| 118 | // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm | |
| 119 | const qpc = self.timestamp - earlier.timestamp; | |
| 120 | const qpf = windows.QueryPerformanceFrequency(); | |
| 121 | ||
| 122 | // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it. | |
| 123 | // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701 | |
| 124 | const common_qpf = 10_000_000; | |
| 125 | if (qpf == common_qpf) { | |
| 126 | return qpc * (ns_per_s / common_qpf); | |
| 127 | } | |
| 128 | ||
| 129 | // Convert to ns using fixed point. | |
| 130 | const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf)); | |
| 131 | const result = (@as(u96, qpc) * scale) >> 32; | |
| 132 | return @as(u64, @truncate(result)); | |
| 133 | }, | |
| 134 | .uefi, .wasi => { | |
| 135 | // UEFI and WASI timestamps are directly in nanoseconds | |
| 136 | return self.timestamp - earlier.timestamp; | |
| 137 | }, | |
| 138 | else => { | |
| 139 | // Convert timespec diff to ns | |
| 140 | const seconds = @as(u64, @intCast(self.timestamp.sec - earlier.timestamp.sec)); | |
| 141 | const elapsed = (seconds * ns_per_s) + @as(u32, @intCast(self.timestamp.nsec)); | |
| 142 | return elapsed - @as(u32, @intCast(earlier.timestamp.nsec)); | |
| 143 | }, | |
| 144 | } | |
| 145 | } | |
| 146 | }; | |
| 147 | ||
| 148 | /// A monotonic, high performance timer. | |
| 149 | /// | |
| 150 | /// Timer.start() is used to initialize the timer | |
| 151 | /// and gives the caller an opportunity to check for the existence of a supported clock. | |
| 152 | /// Once a supported clock is discovered, | |
| 153 | /// it is assumed that it will be available for the duration of the Timer's use. | |
| 154 | /// | |
| 155 | /// Monotonicity is ensured by saturating on the most previous sample. | |
| 156 | /// This means that while timings reported are monotonic, | |
| 157 | /// they're not guaranteed to tick at a steady rate as this is up to the underlying system. | |
| 158 | pub const Timer = struct { | |
| 159 | started: Instant, | |
| 160 | previous: Instant, | |
| 161 | ||
| 162 | pub const Error = error{TimerUnsupported}; | |
| 163 | ||
| 164 | /// Initialize the timer by querying for a supported clock. | |
| 165 | /// Returns `error.TimerUnsupported` when such a clock is unavailable. | |
| 166 | /// This should only fail in hostile environments such as linux seccomp misuse. | |
| 167 | pub fn start() Error!Timer { | |
| 168 | const current = Instant.now() catch return error.TimerUnsupported; | |
| 169 | return Timer{ .started = current, .previous = current }; | |
| 170 | } | |
| 171 | ||
| 172 | /// Reads the timer value since start or the last reset in nanoseconds. | |
| 173 | pub fn read(self: *Timer) u64 { | |
| 174 | const current = self.sample(); | |
| 175 | return current.since(self.started); | |
| 176 | } | |
| 177 | ||
| 178 | /// Resets the timer value to 0/now. | |
| 179 | pub fn reset(self: *Timer) void { | |
| 180 | const current = self.sample(); | |
| 181 | self.started = current; | |
| 182 | } | |
| 183 | ||
| 184 | /// Returns the current value of the timer in nanoseconds, then resets it. | |
| 185 | pub fn lap(self: *Timer) u64 { | |
| 186 | const current = self.sample(); | |
| 187 | defer self.started = current; | |
| 188 | return current.since(self.started); | |
| 189 | } | |
| 190 | ||
| 191 | /// Returns an Instant sampled at the callsite that is | |
| 192 | /// guaranteed to be monotonic with respect to the timer's starting point. | |
| 193 | fn sample(self: *Timer) Instant { | |
| 194 | const current = Instant.now() catch unreachable; | |
| 195 | if (current.order(self.previous) == .gt) { | |
| 196 | self.previous = current; | |
| 197 | } | |
| 198 | return self.previous; | |
| 199 | } | |
| 200 | }; | |
| 201 | ||
| 202 | test Timer { | |
| 203 | const io = std.testing.io; | |
| 204 | ||
| 205 | var timer = try Timer.start(); | |
| 206 | ||
| 207 | try std.Io.Clock.Duration.sleep(.{ .clock = .awake, .raw = .fromMilliseconds(10) }, io); | |
| 208 | const time_0 = timer.read(); | |
| 209 | try testing.expect(time_0 > 0); | |
| 210 | ||
| 211 | const time_1 = timer.lap(); | |
| 212 | try testing.expect(time_1 >= time_0); | |
| 213 | } | |
| 214 | ||
| 215 | 33 | test { |
| 216 | 34 | _ = epoch; |
| 217 | 35 | } |
src/Compilation.zig+15-20| ... | ... | @@ -331,48 +331,42 @@ const QueuedJobs = struct { |
| 331 | 331 | pub const Timer = union(enum) { |
| 332 | 332 | unused, |
| 333 | 333 | active: struct { |
| 334 | start: std.time.Instant, | |
| 334 | start: Io.Timestamp, | |
| 335 | 335 | saved_ns: u64, |
| 336 | 336 | }, |
| 337 | 337 | paused: u64, |
| 338 | 338 | stopped, |
| 339 | 339 | |
| 340 | pub fn pause(t: *Timer) void { | |
| 340 | pub fn pause(t: *Timer, io: Io) void { | |
| 341 | 341 | switch (t.*) { |
| 342 | 342 | .unused => return, |
| 343 | 343 | .active => |a| { |
| 344 | const current = std.time.Instant.now() catch unreachable; | |
| 345 | const new_ns = switch (current.order(a.start)) { | |
| 346 | .lt, .eq => 0, | |
| 347 | .gt => current.since(a.start), | |
| 348 | }; | |
| 344 | const current: Io.Timestamp = .now(io, .awake); | |
| 345 | const new_ns: u64 = @intCast(current.nanoseconds -| a.start.nanoseconds); | |
| 349 | 346 | t.* = .{ .paused = a.saved_ns + new_ns }; |
| 350 | 347 | }, |
| 351 | 348 | .paused => unreachable, |
| 352 | 349 | .stopped => unreachable, |
| 353 | 350 | } |
| 354 | 351 | } |
| 355 | pub fn @"resume"(t: *Timer) void { | |
| 352 | pub fn @"resume"(t: *Timer, io: Io) void { | |
| 356 | 353 | switch (t.*) { |
| 357 | 354 | .unused => return, |
| 358 | 355 | .active => unreachable, |
| 359 | 356 | .paused => |saved_ns| t.* = .{ .active = .{ |
| 360 | .start = std.time.Instant.now() catch unreachable, | |
| 357 | .start = .now(io, .awake), | |
| 361 | 358 | .saved_ns = saved_ns, |
| 362 | 359 | } }, |
| 363 | 360 | .stopped => unreachable, |
| 364 | 361 | } |
| 365 | 362 | } |
| 366 | pub fn finish(t: *Timer) ?u64 { | |
| 363 | pub fn finish(t: *Timer, io: Io) ?u64 { | |
| 367 | 364 | defer t.* = .stopped; |
| 368 | 365 | switch (t.*) { |
| 369 | 366 | .unused => return null, |
| 370 | 367 | .active => |a| { |
| 371 | const current = std.time.Instant.now() catch unreachable; | |
| 372 | const new_ns = switch (current.order(a.start)) { | |
| 373 | .lt, .eq => 0, | |
| 374 | .gt => current.since(a.start), | |
| 375 | }; | |
| 368 | const current: Io.Timestamp = .now(io, .awake); | |
| 369 | const new_ns: u64 = @intCast(current.nanoseconds -| a.start.nanoseconds); | |
| 376 | 370 | return a.saved_ns + new_ns; |
| 377 | 371 | }, |
| 378 | 372 | .paused => |ns| return ns, |
| ... | ... | @@ -387,7 +381,8 @@ pub const Timer = union(enum) { |
| 387 | 381 | /// is set. |
| 388 | 382 | pub fn startTimer(comp: *Compilation) Timer { |
| 389 | 383 | if (comp.time_report == null) return .unused; |
| 390 | const now = std.time.Instant.now() catch @panic("std.time.Timer unsupported; cannot emit time report"); | |
| 384 | const io = comp.io; | |
| 385 | const now: Io.Timestamp = .now(io, .awake); | |
| 391 | 386 | return .{ .active = .{ |
| 392 | 387 | .start = now, |
| 393 | 388 | .saved_ns = 0, |
| ... | ... | @@ -3408,7 +3403,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel |
| 3408 | 3403 | defer sub_prog_node.end(); |
| 3409 | 3404 | |
| 3410 | 3405 | var timer = comp.startTimer(); |
| 3411 | defer if (timer.finish()) |ns| { | |
| 3406 | defer if (timer.finish(io)) |ns| { | |
| 3412 | 3407 | comp.mutex.lockUncancelable(io); |
| 3413 | 3408 | defer comp.mutex.unlock(io); |
| 3414 | 3409 | comp.time_report.?.stats.real_ns_llvm_emit = ns; |
| ... | ... | @@ -3453,7 +3448,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel |
| 3453 | 3448 | } |
| 3454 | 3449 | if (comp.bin_file) |lf| { |
| 3455 | 3450 | var timer = comp.startTimer(); |
| 3456 | defer if (timer.finish()) |ns| { | |
| 3451 | defer if (timer.finish(io)) |ns| { | |
| 3457 | 3452 | comp.mutex.lockUncancelable(io); |
| 3458 | 3453 | defer comp.mutex.unlock(io); |
| 3459 | 3454 | comp.time_report.?.stats.real_ns_link_flush = ns; |
| ... | ... | @@ -4686,7 +4681,7 @@ fn performAllTheWork( |
| 4686 | 4681 | var decl_work_timer: ?Timer = null; |
| 4687 | 4682 | defer commit_timer: { |
| 4688 | 4683 | const t = &(decl_work_timer orelse break :commit_timer); |
| 4689 | const ns = t.finish() orelse break :commit_timer; | |
| 4684 | const ns = t.finish(io) orelse break :commit_timer; | |
| 4690 | 4685 | comp.mutex.lockUncancelable(io); |
| 4691 | 4686 | defer comp.mutex.unlock(io); |
| 4692 | 4687 | comp.time_report.?.stats.real_ns_decls = ns; |
| ... | ... | @@ -4719,7 +4714,7 @@ fn performAllTheWork( |
| 4719 | 4714 | defer zir_prog_node.end(); |
| 4720 | 4715 | |
| 4721 | 4716 | var timer = comp.startTimer(); |
| 4722 | defer if (timer.finish()) |ns| { | |
| 4717 | defer if (timer.finish(io)) |ns| { | |
| 4723 | 4718 | comp.mutex.lockUncancelable(io); |
| 4724 | 4719 | defer comp.mutex.unlock(io); |
| 4725 | 4720 | comp.time_report.?.stats.real_ns_files = ns; |
src/Zcu.zig+6-4| ... | ... | @@ -4754,6 +4754,7 @@ const TrackedUnitSema = struct { |
| 4754 | 4754 | analysis_timer_decl: ?InternPool.TrackedInst.Index, |
| 4755 | 4755 | pub fn end(tus: TrackedUnitSema, zcu: *Zcu) void { |
| 4756 | 4756 | const comp = zcu.comp; |
| 4757 | const io = comp.io; | |
| 4757 | 4758 | if (tus.old_name) |old_name| { |
| 4758 | 4759 | zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion |
| 4759 | 4760 | zcu.cur_sema_prog_node.setName(&old_name); |
| ... | ... | @@ -4762,9 +4763,8 @@ const TrackedUnitSema = struct { |
| 4762 | 4763 | zcu.cur_sema_prog_node = .none; |
| 4763 | 4764 | } |
| 4764 | 4765 | report_time: { |
| 4765 | const sema_ns = zcu.cur_analysis_timer.?.finish() orelse break :report_time; | |
| 4766 | const sema_ns = zcu.cur_analysis_timer.?.finish(io) orelse break :report_time; | |
| 4766 | 4767 | const zir_decl = tus.analysis_timer_decl orelse break :report_time; |
| 4767 | const io = comp.io; | |
| 4768 | 4768 | comp.mutex.lockUncancelable(io); |
| 4769 | 4769 | defer comp.mutex.unlock(io); |
| 4770 | 4770 | comp.time_report.?.stats.cpu_ns_sema += sema_ns; |
| ... | ... | @@ -4779,11 +4779,13 @@ const TrackedUnitSema = struct { |
| 4779 | 4779 | gop.value_ptr.count += 1; |
| 4780 | 4780 | } |
| 4781 | 4781 | zcu.cur_analysis_timer = tus.old_analysis_timer; |
| 4782 | if (zcu.cur_analysis_timer) |*t| t.@"resume"(); | |
| 4782 | if (zcu.cur_analysis_timer) |*t| t.@"resume"(io); | |
| 4783 | 4783 | } |
| 4784 | 4784 | }; |
| 4785 | 4785 | pub fn trackUnitSema(zcu: *Zcu, name: []const u8, zir_inst: ?InternPool.TrackedInst.Index) TrackedUnitSema { |
| 4786 | if (zcu.cur_analysis_timer) |*t| t.pause(); | |
| 4786 | const comp = zcu.comp; | |
| 4787 | const io = comp.io; | |
| 4788 | if (zcu.cur_analysis_timer) |*t| t.pause(io); | |
| 4787 | 4789 | const old_analysis_timer = zcu.cur_analysis_timer; |
| 4788 | 4790 | zcu.cur_analysis_timer = zcu.comp.startTimer(); |
| 4789 | 4791 | const old_name: ?[std.Progress.Node.max_name_len]u8 = old_name: { |
src/Zcu/PerThread.zig+3-3| ... | ... | @@ -263,7 +263,7 @@ pub fn updateFile( |
| 263 | 263 | var timer = comp.startTimer(); |
| 264 | 264 | // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen. |
| 265 | 265 | file.tree = try Ast.parse(gpa, source, file.getMode()); |
| 266 | if (timer.finish()) |ns_parse| { | |
| 266 | if (timer.finish(io)) |ns_parse| { | |
| 267 | 267 | comp.mutex.lockUncancelable(io); |
| 268 | 268 | defer comp.mutex.unlock(io); |
| 269 | 269 | comp.time_report.?.stats.cpu_ns_parse += ns_parse; |
| ... | ... | @@ -295,7 +295,7 @@ pub fn updateFile( |
| 295 | 295 | else => |e| return e, |
| 296 | 296 | }; |
| 297 | 297 | |
| 298 | if (timer.finish()) |ns_astgen| { | |
| 298 | if (timer.finish(io)) |ns_astgen| { | |
| 299 | 299 | comp.mutex.lockUncancelable(io); |
| 300 | 300 | defer comp.mutex.unlock(io); |
| 301 | 301 | comp.time_report.?.stats.cpu_ns_astgen += ns_astgen; |
| ... | ... | @@ -4485,7 +4485,7 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) Ru |
| 4485 | 4485 | |
| 4486 | 4486 | const codegen_result = runCodegenInner(pt, func_index, air); |
| 4487 | 4487 | |
| 4488 | if (timer.finish()) |ns_codegen| report_time: { | |
| 4488 | if (timer.finish(io)) |ns_codegen| report_time: { | |
| 4489 | 4489 | const ip = &zcu.intern_pool; |
| 4490 | 4490 | const nav = ip.indexToKey(func_index).func.owner_nav; |
| 4491 | 4491 | const zir_decl = ip.getNav(nav).srcInst(ip); |
src/link.zig+4-4| ... | ... | @@ -1388,7 +1388,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { |
| 1388 | 1388 | }; |
| 1389 | 1389 | |
| 1390 | 1390 | var timer = comp.startTimer(); |
| 1391 | defer if (timer.finish()) |ns| { | |
| 1391 | defer if (timer.finish(io)) |ns| { | |
| 1392 | 1392 | comp.mutex.lockUncancelable(io); |
| 1393 | 1393 | defer comp.mutex.unlock(io); |
| 1394 | 1394 | comp.time_report.?.stats.cpu_ns_link += ns; |
| ... | ... | @@ -1535,12 +1535,12 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { |
| 1535 | 1535 | break :nav nav_index; |
| 1536 | 1536 | }, |
| 1537 | 1537 | .link_func => |codegen_task| nav: { |
| 1538 | timer.pause(); | |
| 1538 | timer.pause(io); | |
| 1539 | 1539 | const func, var mir = codegen_task.wait(&zcu.codegen_task_pool, io) catch |err| switch (err) { |
| 1540 | 1540 | error.Canceled, error.AlreadyReported => return, |
| 1541 | 1541 | }; |
| 1542 | 1542 | defer mir.deinit(zcu); |
| 1543 | timer.@"resume"(); | |
| 1543 | timer.@"resume"(io); | |
| 1544 | 1544 | |
| 1545 | 1545 | const nav = zcu.funcInfo(func).owner_nav; |
| 1546 | 1546 | const fqn_slice = ip.getNav(nav).fqn.toSlice(ip); |
| ... | ... | @@ -1592,7 +1592,7 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { |
| 1592 | 1592 | }, |
| 1593 | 1593 | }; |
| 1594 | 1594 | |
| 1595 | if (timer.finish()) |ns_link| report_time: { | |
| 1595 | if (timer.finish(io)) |ns_link| report_time: { | |
| 1596 | 1596 | comp.mutex.lockUncancelable(io); |
| 1597 | 1597 | defer comp.mutex.unlock(io); |
| 1598 | 1598 | const tr = &zcu.comp.time_report.?; |
stage1/wasi.c+9| ... | ... | @@ -924,6 +924,15 @@ uint32_t wasi_snapshot_preview1_clock_time_get(uint32_t id, uint64_t precision, |
| 924 | 924 | return wasi_errno_success; |
| 925 | 925 | } |
| 926 | 926 | |
| 927 | uint32_t wasi_snapshot_preview1_clock_res_get(uint32_t id, uint32_t res_timestamp) { | |
| 928 | uint8_t *const m = *wasm_memory; | |
| 929 | uint64_t *res_timestamp_ptr = (uint64_t *)&m[res_timestamp]; | |
| 930 | #if LOG_TRACE | |
| 931 | fprintf(stderr, "wasi_snapshot_preview1_clock_res_get(%u, %llu)\n", id, (unsigned long long)res_timestamp); | |
| 932 | #endif | |
| 933 | return wasi_errno_notcapable; | |
| 934 | } | |
| 935 | ||
| 927 | 936 | uint32_t wasi_snapshot_preview1_path_remove_directory(uint32_t fd, uint32_t path, uint32_t path_len) { |
| 928 | 937 | uint8_t *const m = *wasm_memory; |
| 929 | 938 | const char *path_ptr = (const char *)&m[path]; |