authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-03 20:16:18+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-03 20:16:18+01:00
loge5e4602b181360d20bbba56e9f15734c929d2a2f
treef124eb04f53acf6b8029ddaef027a21c78f934d2
parent7aae7dd3f4d4b85837369fa591e566ae812f87dc
parentfe5da36aa3b1dbeb02276c6628640c68a5922191

Merge pull request 'std: finish moving time to Io interface' (#31086) from time into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31086

23 files changed, 272 insertions(+), 406 deletions(-)

lib/compiler/aro/aro/Compilation.zig+1-1
...@@ -107,7 +107,7 @@ pub const Environment = struct {...@@ -107,7 +107,7 @@ pub const Environment = struct {
107 if (parsed > max_timestamp) return error.InvalidEpoch;107 if (parsed > max_timestamp) return error.InvalidEpoch;
108 return .{ .provided = parsed };108 return .{ .provided = parsed };
109 } else {109 } else {
110 const timestamp = try Io.Clock.real.now(io);110 const timestamp = Io.Clock.real.now(io);
111 const seconds = std.math.cast(u64, timestamp.toSeconds()) orelse return error.InvalidEpoch;111 const seconds = std.math.cast(u64, timestamp.toSeconds()) orelse return error.InvalidEpoch;
112 return .{ .system = std.math.clamp(seconds, 0, max_timestamp) };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,7 +301,7 @@ pub fn init(comp: *Compilation, source_epoch: SourceEpoch) Preprocessor {
301/// Initialize Preprocessor with builtin macros.301/// Initialize Preprocessor with builtin macros.
302pub fn initDefault(comp: *Compilation) !Preprocessor {302pub fn initDefault(comp: *Compilation) !Preprocessor {
303 const source_epoch: SourceEpoch = comp.environment.sourceEpoch(comp.io) catch |er| switch (er) {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 const diagnostic: Diagnostic = .invalid_source_epoch;305 const diagnostic: Diagnostic = .invalid_source_epoch;
306 try comp.diagnostics.add(.{ .text = diagnostic.fmt, .kind = diagnostic.kind, .opt = diagnostic.opt, .location = null });306 try comp.diagnostics.add(.{ .text = diagnostic.fmt, .kind = diagnostic.kind, .opt = diagnostic.opt, .location = null });
307 break :blk .default;307 break :blk .default;
lib/compiler/build_runner.zig+1-1
...@@ -548,7 +548,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -548,7 +548,7 @@ pub fn main(init: process.Init.Minimal) !void {
548 break :w try .init(graph.cache.cwd);548 break :w try .init(graph.cache.cwd);
549 };549 };
550550
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);
552552
553 run.web_server = if (webui_listen) |listen_address| ws: {553 run.web_server = if (webui_listen) |listen_address| ws: {
554 if (builtin.single_threaded) unreachable; // `fatal` above554 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,16 +266,19 @@ pub fn init(options: StepOptions) Step {
266/// here.266/// here.
267pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void {267pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void {
268 const arena = s.owner.allocator;268 const arena = s.owner.allocator;
269 const graph = s.owner.graph;
270 const io = graph.io;
269271
270 var timer: ?std.time.Timer = t: {272 var start_ts: ?Io.Timestamp = t: {
271 if (!s.owner.graph.time_report) break :t null;273 if (!graph.time_report) break :t null;
272 if (s.id == .compile) break :t null;274 if (s.id == .compile) break :t null;
273 if (s.id == .run and s.cast(Run).?.stdio == .zig_test) break :t null;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 const make_result = s.makeFn(s, options);278 const make_result = s.makeFn(s, options);
277 if (timer) |*t| {279 if (start_ts) |*ts| {
278 options.web_server.?.updateTimeReportGeneric(s, t.read());280 const duration = ts.untilNow(io, .awake);
281 options.web_server.?.updateTimeReportGeneric(s, duration);
279 }282 }
280283
281 make_result catch |err| switch (err) {284 make_result catch |err| switch (err) {
...@@ -534,7 +537,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build....@@ -534,7 +537,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.
534 const arena = b.allocator;537 const arena = b.allocator;
535 const io = b.graph.io;538 const io = b.graph.io;
536539
537 var timer = try std.time.Timer.start();540 const start_ts = Io.Clock.awake.now(io);
538541
539 try sendMessage(io, zp.child.stdin.?, .update);542 try sendMessage(io, zp.child.stdin.?, .update);
540 if (!watch) try sendMessage(io, zp.child.stdin.?, .exit);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,7 +640,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.
637 .compile = s.cast(Step.Compile).?,640 .compile = s.cast(Step.Compile).?,
638 .use_llvm = tr.flags.use_llvm,641 .use_llvm = tr.flags.use_llvm,
639 .stats = tr.stats,642 .stats = tr.stats,
640 .ns_total = timer.read(),643 .ns_total = @intCast(start_ts.untilNow(io, .awake).toNanoseconds()),
641 .llvm_pass_timings_len = tr.llvm_pass_timings_len,644 .llvm_pass_timings_len = tr.llvm_pass_timings_len,
642 .files_len = tr.files_len,645 .files_len = tr.files_len,
643 .decls_len = tr.decls_len,646 .decls_len = tr.decls_len,
...@@ -648,7 +651,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build....@@ -648,7 +651,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*Build.
648 }651 }
649 }652 }
650653
651 s.result_duration_ns = timer.read();654 s.result_duration_ns = @intCast(start_ts.untilNow(io, .awake).toNanoseconds());
652655
653 const stderr_contents = zp.multi_reader.reader(1).buffered();656 const stderr_contents = zp.multi_reader.reader(1).buffered();
654 if (stderr_contents.len > 0) {657 if (stderr_contents.len > 0) {
lib/std/Build/Step/Run.zig+13-13
...@@ -1587,12 +1587,12 @@ fn spawnChildAndCollect(...@@ -1587,12 +1587,12 @@ fn spawnChildAndCollect(
1587 };1587 };
15881588
1589 if (run.stdio == .zig_test) {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 const result = evalZigTest(run, spawn_options, options, fuzz_context) catch |err| switch (err) {1591 const result = evalZigTest(run, spawn_options, options, fuzz_context) catch |err| switch (err) {
1592 error.Canceled => |e| return e,1592 error.Canceled => |e| return e,
1593 else => |e| e,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 try result;1596 try result;
1597 return null;1597 return null;
1598 } else {1598 } else {
...@@ -1607,12 +1607,12 @@ fn spawnChildAndCollect(...@@ -1607,12 +1607,12 @@ fn spawnChildAndCollect(
1607 defer if (inherit) io.unlockStderr();1607 defer if (inherit) io.unlockStderr();
1608 try setColorEnvironmentVariables(run, environ_map, terminal_mode);1608 try setColorEnvironmentVariables(run, environ_map, terminal_mode);
16091609
1610 const started: Io.Clock.Timestamp = try .now(io, .awake);1610 const started: Io.Clock.Timestamp = .now(io, .awake);
1611 const result = evalGeneric(run, spawn_options) catch |err| switch (err) {1611 const result = evalGeneric(run, spawn_options) catch |err| switch (err) {
1612 error.Canceled => |e| return e,1612 error.Canceled => |e| return e,
1613 else => |e| e,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 return try result;1616 return try result;
1617 }1617 }
1618}1618}
...@@ -1869,7 +1869,7 @@ fn waitZigTest(...@@ -1869,7 +1869,7 @@ fn waitZigTest(
18691869
1870 var active_test_index: ?u32 = null;1870 var active_test_index: ?u32 = null;
18711871
1872 var last_update: Io.Clock.Timestamp = try .now(io, .awake);1872 var last_update: Io.Clock.Timestamp = .now(io, .awake);
18731873
1874 var coverage_id: ?u64 = null;1874 var coverage_id: ?u64 = null;
18751875
...@@ -1908,11 +1908,11 @@ fn waitZigTest(...@@ -1908,11 +1908,11 @@ fn waitZigTest(
1908 multi_reader.fill(64, timeout) catch |err| switch (err) {1908 multi_reader.fill(64, timeout) catch |err| switch (err) {
1909 error.Timeout => return .{ .timeout = .{1909 error.Timeout => return .{ .timeout = .{
1910 .active_test_index = active_test_index,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 error.EndOfStream => return .{ .no_poll = .{1913 error.EndOfStream => return .{ .no_poll = .{
1914 .active_test_index = active_test_index,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 else => |e| return e,1917 else => |e| return e,
1918 };1918 };
...@@ -1926,11 +1926,11 @@ fn waitZigTest(...@@ -1926,11 +1926,11 @@ fn waitZigTest(
1926 multi_reader.fill(64, timeout) catch |err| switch (err) {1926 multi_reader.fill(64, timeout) catch |err| switch (err) {
1927 error.Timeout => return .{ .timeout = .{1927 error.Timeout => return .{ .timeout = .{
1928 .active_test_index = active_test_index,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 error.EndOfStream => return .{ .no_poll = .{1931 error.EndOfStream => return .{ .no_poll = .{
1932 .active_test_index = active_test_index,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 else => |e| return e,1935 else => |e| return e,
1936 };1936 };
...@@ -1976,13 +1976,13 @@ fn waitZigTest(...@@ -1976,13 +1976,13 @@ fn waitZigTest(
1976 @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64));1976 @memset(opt_metadata.*.?.ns_per_test, std.math.maxInt(u64));
19771977
1978 active_test_index = null;1978 active_test_index = null;
1979 last_update = try .now(io, .awake);1979 last_update = .now(io, .awake);
19801980
1981 requestNextTest(io, child.stdin.?, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err };1981 requestNextTest(io, child.stdin.?, &opt_metadata.*.?, &sub_prog_node) catch |err| return .{ .write_failed = err };
1982 },1982 },
1983 .test_started => {1983 .test_started => {
1984 active_test_index = opt_metadata.*.?.next_index - 1;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 .test_results => {1987 .test_results => {
1988 assert(fuzz_context == null);1988 assert(fuzz_context == null);
...@@ -2026,7 +2026,7 @@ fn waitZigTest(...@@ -2026,7 +2026,7 @@ fn waitZigTest(
20262026
2027 active_test_index = null;2027 active_test_index = null;
20282028
2029 const now: Io.Clock.Timestamp = try .now(io, .awake);2029 const now: Io.Clock.Timestamp = .now(io, .awake);
2030 md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds);2030 md.ns_per_test[tr_hdr.index] = @intCast(last_update.durationTo(now).raw.nanoseconds);
2031 last_update = now;2031 last_update = now;
20322032
...@@ -2239,7 +2239,7 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul...@@ -2239,7 +2239,7 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul
2239 return error.StderrStreamTooLong;2239 return error.StderrStreamTooLong;
2240 }2240 }
2241 } else |err| switch (err) {2241 } else |err| switch (err) {
2242 error.UnsupportedClock, error.Timeout => unreachable,2242 error.Timeout => unreachable,
2243 error.EndOfStream => {},2243 error.EndOfStream => {},
2244 else => |e| return e,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,7 +243,7 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
243243
244pub fn now(s: *const WebServer) i64 {244pub fn now(s: *const WebServer) i64 {
245 const io = s.graph.io;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 return @intCast(s.base_timestamp.durationTo(ts).toNanoseconds());247 return @intCast(s.base_timestamp.durationTo(ts).toNanoseconds());
248}248}
249249
...@@ -761,7 +761,7 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {...@@ -761,7 +761,7 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct {
761 ws.notifyUpdate();761 ws.notifyUpdate();
762}762}
763763
764pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64) void {764pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, duration: Io.Duration) void {
765 const gpa = ws.gpa;765 const gpa = ws.gpa;
766 const io = ws.graph.io;766 const io = ws.graph.io;
767767
...@@ -780,7 +780,7 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64)...@@ -780,7 +780,7 @@ pub fn updateTimeReportGeneric(ws: *WebServer, step: *Build.Step, ns_total: u64)
780 const out: *align(1) abi.time_report.GenericResult = @ptrCast(buf);780 const out: *align(1) abi.time_report.GenericResult = @ptrCast(buf);
781 out.* = .{781 out.* = .{
782 .step_idx = step_idx,782 .step_idx = step_idx,
783 .ns_total = ns_total,783 .ns_total = @intCast(duration.toNanoseconds()),
784 };784 };
785 {785 {
786 ws.time_report_mutex.lock(io) catch return;786 ws.time_report_mutex.lock(io) catch return;
lib/std/Io.zig+93-34
...@@ -231,8 +231,9 @@ pub const VTable = struct {...@@ -231,8 +231,9 @@ pub const VTable = struct {
231231
232 progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File,232 progressParentFile: *const fn (?*anyopaque) std.Progress.ParentFileError!File,
233233
234 now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,234 now: *const fn (?*anyopaque, Clock) Timestamp,
235 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,235 clockResolution: *const fn (?*anyopaque, Clock) Clock.ResolutionError!Duration,
236 sleep: *const fn (?*anyopaque, Timeout) Cancelable!void,
236237
237 random: *const fn (?*anyopaque, buffer: []u8) void,238 random: *const fn (?*anyopaque, buffer: []u8) void,
238 randomSecure: *const fn (?*anyopaque, buffer: []u8) RandomSecureError!void,239 randomSecure: *const fn (?*anyopaque, buffer: []u8) RandomSecureError!void,
...@@ -701,30 +702,53 @@ pub const Clock = enum {...@@ -701,30 +702,53 @@ pub const Clock = enum {
701 /// thread.702 /// thread.
702 cpu_thread,703 cpu_thread,
703704
704 pub const Error = error{UnsupportedClock} || UnexpectedError;705 /// This function is not cancelable because it does not block.
705706 ///
706 /// This function is not cancelable because first of all it does not block,707 /// Resolution is determined by `resolution` which may be 0 if the
707 /// but more importantly, the cancelation logic itself may want to check708 /// clock is unsupported.
708 /// the time.709 ///
709 pub fn now(clock: Clock, io: Io) Error!Io.Timestamp {710 /// See also:
711 /// * `Clock.Timestamp.now`
712 pub fn now(clock: Clock, io: Io) Io.Timestamp {
710 return io.vtable.now(io.userdata, clock);713 return io.vtable.now(io.userdata, clock);
711 }714 }
712715
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 pub const Timestamp = struct {727 pub const Timestamp = struct {
714 raw: Io.Timestamp,728 raw: Io.Timestamp,
715 clock: Clock,729 clock: Clock,
716730
717 /// This function is not cancelable because first of all it does not block,731 /// This function is not cancelable because it does not block.
718 /// but more importantly, the cancelation logic itself may want to check732 ///
719 /// the time.733 /// Resolution is determined by `resolution` which may be 0 if
720 pub fn now(io: Io, clock: Clock) Error!Clock.Timestamp {734 /// the clock is unsupported.
735 ///
736 /// See also:
737 /// * `Clock.now`
738 pub fn now(io: Io, clock: Clock) Clock.Timestamp {
721 return .{739 return .{
722 .raw = try io.vtable.now(io.userdata, clock),740 .raw = io.vtable.now(io.userdata, clock),
723 .clock = clock,741 .clock = clock,
724 };742 };
725 }743 }
726744
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 return io.vtable.sleep(io.userdata, .{ .deadline = t });752 return io.vtable.sleep(io.userdata, .{ .deadline = t });
729 }753 }
730754
...@@ -752,30 +776,38 @@ pub const Clock = enum {...@@ -752,30 +776,38 @@ pub const Clock = enum {
752 };776 };
753 }777 }
754778
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 return .{782 return .{
757 .clock = duration.clock,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 }
761787
762 pub fn untilNow(timestamp: Clock.Timestamp, io: Io) Error!Clock.Duration {788 /// Resolution is determined by `resolution` which may be 0 if
763 const now_ts = try Clock.Timestamp.now(io, timestamp.clock);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 return timestamp.durationTo(now_ts);792 return timestamp.durationTo(now_ts);
765 }793 }
766794
767 pub fn durationFromNow(timestamp: Clock.Timestamp, io: Io) Error!Clock.Duration {795 /// Resolution is determined by `resolution` which may be 0 if
768 const now_ts = try timestamp.clock.now(io);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 return .{799 return .{
770 .clock = timestamp.clock,800 .clock = timestamp.clock,
771 .raw = now_ts.durationTo(timestamp.raw),801 .raw = now_ts.durationTo(timestamp.raw),
772 };802 };
773 }803 }
774804
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 if (t.clock == clock) return t;808 if (t.clock == clock) return t;
777 const now_old = try t.clock.now(io);809 const now_old = t.clock.now(io);
778 const now_new = try clock.now(io);810 const now_new = clock.now(io);
779 const duration = now_old.durationTo(t);811 const duration = now_old.durationTo(t);
780 return .{812 return .{
781 .clock = clock,813 .clock = clock,
...@@ -793,7 +825,13 @@ pub const Clock = enum {...@@ -793,7 +825,13 @@ pub const Clock = enum {
793 raw: Io.Duration,825 raw: Io.Duration,
794 clock: Clock,826 clock: Clock,
795827
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 return io.vtable.sleep(io.userdata, .{ .duration = duration });835 return io.vtable.sleep(io.userdata, .{ .duration = duration });
798 }836 }
799 };837 };
...@@ -802,6 +840,10 @@ pub const Clock = enum {...@@ -802,6 +840,10 @@ pub const Clock = enum {
802pub const Timestamp = struct {840pub const Timestamp = struct {
803 nanoseconds: i96,841 nanoseconds: i96,
804842
843 pub fn now(io: Io, clock: Clock) Io.Timestamp {
844 return io.vtable.now(io.userdata, clock);
845 }
846
805 pub const zero: Timestamp = .{ .nanoseconds = 0 };847 pub const zero: Timestamp = .{ .nanoseconds = 0 };
806848
807 pub fn durationTo(from: Timestamp, to: Timestamp) Duration {849 pub fn durationTo(from: Timestamp, to: Timestamp) Duration {
...@@ -844,6 +886,13 @@ pub const Timestamp = struct {...@@ -844,6 +886,13 @@ pub const Timestamp = struct {
844 .fill = n.fill,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};
848897
849pub const Duration = struct {898pub const Duration = struct {
...@@ -883,12 +932,12 @@ pub const Timeout = union(enum) {...@@ -883,12 +932,12 @@ pub const Timeout = union(enum) {
883 duration: Clock.Duration,932 duration: Clock.Duration,
884 deadline: Clock.Timestamp,933 deadline: Clock.Timestamp,
885934
886 pub const Error = error{ Timeout, UnsupportedClock };935 pub const Error = error{Timeout};
887936
888 pub fn toTimestamp(t: Timeout, io: Io) Clock.Error!?Clock.Timestamp {937 pub fn toTimestamp(t: Timeout, io: Io) ?Clock.Timestamp {
889 return switch (t) {938 return switch (t) {
890 .none => null,939 .none => null,
891 .duration => |d| try .fromNow(io, d),940 .duration => |d| .fromNow(io, d),
892 .deadline => |d| d,941 .deadline => |d| d,
893 };942 };
894 }943 }
...@@ -896,20 +945,26 @@ pub const Timeout = union(enum) {...@@ -896,20 +945,26 @@ pub const Timeout = union(enum) {
896 pub fn toDeadline(t: Timeout, io: Io) Timeout {945 pub fn toDeadline(t: Timeout, io: Io) Timeout {
897 return switch (t) {946 return switch (t) {
898 .none => .none,947 .none => .none,
899 .duration => |d| .{ .deadline = Clock.Timestamp.fromNow(io, d) catch @panic("TODO") },948 .duration => |d| .{ .deadline = .fromNow(io, d) },
900 .deadline => |d| .{ .deadline = d },949 .deadline => |d| .{ .deadline = d },
901 };950 };
902 }951 }
903952
904 pub fn toDurationFromNow(t: Timeout, io: Io) Clock.Error!?Clock.Duration {953 pub fn toDurationFromNow(t: Timeout, io: Io) ?Clock.Duration {
905 return switch (t) {954 return switch (t) {
906 .none => null,955 .none => null,
907 .duration => |d| d,956 .duration => |d| d,
908 .deadline => |d| try d.durationFromNow(io),957 .deadline => |d| d.durationFromNow(io),
909 };958 };
910 }959 }
911960
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 return io.vtable.sleep(io.userdata, timeout);968 return io.vtable.sleep(io.userdata, timeout);
914 }969 }
915};970};
...@@ -2027,9 +2082,13 @@ pub fn concurrent(...@@ -2027,9 +2082,13 @@ pub fn concurrent(
2027 return future;2082 return future;
2028}2083}
20292084
2030pub const SleepError = error{UnsupportedClock} || UnexpectedError || Cancelable;2085/// Waits until a specified amount of time has passed on `clock`.
20312086///
2032pub fn sleep(io: Io, duration: Duration, clock: Clock) SleepError!void {2087/// See also:
2088/// * `Clock.Duration.sleep`
2089/// * `Clock.Timestamp.wait`
2090/// * `Timeout.sleep`
2091pub fn sleep(io: Io, duration: Duration, clock: Clock) Cancelable!void {
2033 return io.vtable.sleep(io.userdata, .{ .duration = .{2092 return io.vtable.sleep(io.userdata, .{ .duration = .{
2034 .raw = duration,2093 .raw = duration,
2035 .clock = clock,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,7 +179,7 @@ fn rebase(r: *Io.Reader, capacity: usize) Io.Reader.RebaseError!void {
179179
180fn fillUntimed(context: *Context, capacity: usize) Io.Reader.Error!void {180fn fillUntimed(context: *Context, capacity: usize) Io.Reader.Error!void {
181 fill(context.mr, capacity, .none) catch |err| switch (err) {181 fill(context.mr, capacity, .none) catch |err| switch (err) {
182 error.Timeout, error.UnsupportedClock => unreachable,182 error.Timeout => unreachable,
183 error.Canceled, error.ConcurrencyUnavailable => |e| {183 error.Canceled, error.ConcurrencyUnavailable => |e| {
184 context.err = e;184 context.err = e;
185 return error.ReadFailed;185 return error.ReadFailed;
lib/std/Io/Threaded.zig+99-61
...@@ -1712,6 +1712,7 @@ pub fn io(t: *Threaded) Io {...@@ -1712,6 +1712,7 @@ pub fn io(t: *Threaded) Io {
1712 .progressParentFile = progressParentFile,1712 .progressParentFile = progressParentFile,
17131713
1714 .now = now,1714 .now = now,
1715 .clockResolution = clockResolution,
1715 .sleep = sleep,1716 .sleep = sleep,
17161717
1717 .random = random,1718 .random = random,
...@@ -1875,6 +1876,7 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -1875,6 +1876,7 @@ pub fn ioBasic(t: *Threaded) Io {
1875 .progressParentFile = progressParentFile,1876 .progressParentFile = progressParentFile,
18761877
1877 .now = now,1878 .now = now,
1879 .clockResolution = clockResolution,
1878 .sleep = sleep,1880 .sleep = sleep,
18791881
1880 .random = random,1882 .random = random,
...@@ -2487,7 +2489,7 @@ fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io....@@ -2487,7 +2489,7 @@ fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io.
2487 const t: *Threaded = @ptrCast(@alignCast(userdata));2489 const t: *Threaded = @ptrCast(@alignCast(userdata));
2488 const t_io = ioBasic(t);2490 const t_io = ioBasic(t);
2489 const timeout_ns: ?u64 = ns: {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 break :ns std.math.lossyCast(u64, d.raw.toNanoseconds());2493 break :ns std.math.lossyCast(u64, d.raw.toNanoseconds());
2492 };2494 };
2493 return Thread.futexWait(ptr, expected, timeout_ns);2495 return Thread.futexWait(ptr, expected, timeout_ns);
...@@ -2655,24 +2657,12 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {...@@ -2655,24 +2657,12 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
2655fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.AwaitConcurrentError!void {2657fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.AwaitConcurrentError!void {
2656 const t: *Threaded = @ptrCast(@alignCast(userdata));2658 const t: *Threaded = @ptrCast(@alignCast(userdata));
2657 if (is_windows) {2659 if (is_windows) {
2658 const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t)) catch |err| switch (err) {2660 const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(ioBasic(t));
2659 error.Unexpected => deadline: {
2660 recoverableOsBugDetected();
2661 break :deadline .{ .raw = .{ .nanoseconds = 0 }, .clock = .awake };
2662 },
2663 error.UnsupportedClock => |e| return e,
2664 };
2665 try batchAwaitWindows(b, true);2661 try batchAwaitWindows(b, true);
2666 while (b.pending.head != .none and b.completions.head == .none) {2662 while (b.pending.head != .none and b.completions.head == .none) {
2667 var delay_interval: windows.LARGE_INTEGER = interval: {2663 var delay_interval: windows.LARGE_INTEGER = interval: {
2668 const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER);2664 const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER);
2669 break :interval t.deadlineToWindowsInterval(d) catch |err| switch (err) {2665 break :interval t.deadlineToWindowsInterval(d);
2670 error.UnsupportedClock => |e| return e,
2671 error.Unexpected => {
2672 recoverableOsBugDetected();
2673 break :interval -1;
2674 },
2675 };
2676 };2666 };
2677 const alertable_syscall = try AlertableSyscall.start();2667 const alertable_syscall = try AlertableSyscall.start();
2678 const delay_rc = windows.ntdll.NtDelayExecution(windows.TRUE, &delay_interval);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,7 +2744,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
2754 else => {},2744 else => {},
2755 }2745 }
2756 const t_io = ioBasic(t);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 while (true) {2748 while (true) {
2759 const timeout_ms: i32 = t: {2749 const timeout_ms: i32 = t: {
2760 if (b.completions.head != .none) {2750 if (b.completions.head != .none) {
...@@ -2765,7 +2755,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout...@@ -2765,7 +2755,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
2765 break :t 0;2755 break :t 0;
2766 }2756 }
2767 const d = deadline orelse break :t -1;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 if (duration.raw.nanoseconds <= 0) return error.Timeout;2759 if (duration.raw.nanoseconds <= 0) return error.Timeout;
2770 const max_poll_ms = std.math.maxInt(i32);2760 const max_poll_ms = std.math.maxInt(i32);
2771 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));2761 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));
...@@ -10821,22 +10811,21 @@ fn fileWriteFilePositional(...@@ -10821,22 +10811,21 @@ fn fileWriteFilePositional(
10821 return error.Unimplemented;10811 return error.Unimplemented;
10822}10812}
1082310813
10824fn nowPosix(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {10814fn nowPosix(clock: Io.Clock) Io.Timestamp {
10825 const clock_id: posix.clockid_t = clockToPosix(clock);10815 const clock_id: posix.clockid_t = clockToPosix(clock);
10826 var tp: posix.timespec = undefined;10816 var timespec: posix.timespec = undefined;
10827 switch (posix.errno(posix.system.clock_gettime(clock_id, &tp))) {10817 switch (posix.errno(posix.system.clock_gettime(clock_id, &timespec))) {
10828 .SUCCESS => return timestampFromPosix(&tp),10818 .SUCCESS => return timestampFromPosix(&timespec),
10829 .INVAL => return error.UnsupportedClock,10819 else => return .zero,
10830 else => |err| return posix.unexpectedErrno(err),
10831 }10820 }
10832}10821}
1083310822
10834fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {10823fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Timestamp {
10835 const t: *Threaded = @ptrCast(@alignCast(userdata));10824 const t: *Threaded = @ptrCast(@alignCast(userdata));
10836 _ = t;10825 _ = t;
10837 return nowInner(clock);10826 return nowInner(clock);
10838}10827}
10839fn nowInner(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {10828fn nowInner(clock: Io.Clock) Io.Timestamp {
10840 return switch (native_os) {10829 return switch (native_os) {
10841 .windows => nowWindows(clock),10830 .windows => nowWindows(clock),
10842 .wasi => nowWasi(clock),10831 .wasi => nowWasi(clock),
...@@ -10844,7 +10833,55 @@ fn nowInner(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {...@@ -10844,7 +10833,55 @@ fn nowInner(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
10844 };10833 };
10845}10834}
1084610835
10847fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {10836fn 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
10875fn 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
10884fn nowWindows(clock: Io.Clock) Io.Timestamp {
10848 switch (clock) {10885 switch (clock) {
10849 .real => {10886 .real => {
10850 // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds10887 // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds
...@@ -10882,8 +10919,7 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {...@@ -10882,8 +10919,7 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
10882 &times,10919 &times,
10883 @sizeOf(windows.KERNEL_USER_TIMES),10920 @sizeOf(windows.KERNEL_USER_TIMES),
10884 null,10921 null,
10885 ) != .SUCCESS)10922 ) != .SUCCESS) return .zero;
10886 return error.Unexpected;
1088710923
10888 const sum = @as(i96, times.UserTime) + @as(i96, times.KernelTime);10924 const sum = @as(i96, times.UserTime) + @as(i96, times.KernelTime);
10889 return .{ .nanoseconds = sum * 100 };10925 return .{ .nanoseconds = sum * 100 };
...@@ -10899,8 +10935,7 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {...@@ -10899,8 +10935,7 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
10899 &times,10935 &times,
10900 @sizeOf(windows.KERNEL_USER_TIMES),10936 @sizeOf(windows.KERNEL_USER_TIMES),
10901 null,10937 null,
10902 ) != .SUCCESS)10938 ) != .SUCCESS) return .zero;
10903 return error.Unexpected;
1090410939
10905 const sum = @as(i96, times.UserTime) + @as(i96, times.KernelTime);10940 const sum = @as(i96, times.UserTime) + @as(i96, times.KernelTime);
10906 return .{ .nanoseconds = sum * 100 };10941 return .{ .nanoseconds = sum * 100 };
...@@ -10908,23 +10943,23 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {...@@ -10908,23 +10943,23 @@ fn nowWindows(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
10908 }10943 }
10909}10944}
1091010945
10911fn nowWasi(clock: Io.Clock) Io.Clock.Error!Io.Timestamp {10946fn nowWasi(clock: Io.Clock) Io.Timestamp {
10912 var ns: std.os.wasi.timestamp_t = undefined;10947 var ns: std.os.wasi.timestamp_t = undefined;
10913 const err = std.os.wasi.clock_time_get(clockToWasi(clock), 1, &ns);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 return .fromNanoseconds(ns);10950 return .fromNanoseconds(ns);
10916}10951}
1091710952
10918fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {10953fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.Cancelable!void {
10919 const t: *Threaded = @ptrCast(@alignCast(userdata));10954 const t: *Threaded = @ptrCast(@alignCast(userdata));
10920 if (timeout == .none) return;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 if (native_os == .wasi) return sleepWasi(t, timeout);10957 if (native_os == .wasi) return sleepWasi(t, timeout);
10923 if (@TypeOf(posix.system.clock_nanosleep) != void) return sleepPosix(timeout);10958 if (@TypeOf(posix.system.clock_nanosleep) != void) return sleepPosix(timeout);
10924 return sleepNanosleep(t, timeout);10959 return sleepNanosleep(t, timeout);
10925}10960}
1092610961
10927fn sleepPosix(timeout: Io.Timeout) Io.SleepError!void {10962fn sleepPosix(timeout: Io.Timeout) Io.Cancelable!void {
10928 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {10963 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
10929 .none => .awake,10964 .none => .awake,
10930 .duration => |d| d.clock,10965 .duration => |d| d.clock,
...@@ -10944,25 +10979,27 @@ fn sleepPosix(timeout: Io.Timeout) Io.SleepError!void {...@@ -10944,25 +10979,27 @@ fn sleepPosix(timeout: Io.Timeout) Io.SleepError!void {
10944 } }, &timespec, &timespec);10979 } }, &timespec, &timespec);
10945 // POSIX-standard libc clock_nanosleep() returns *positive* errno values directly10980 // POSIX-standard libc clock_nanosleep() returns *positive* errno values directly
10946 switch (if (builtin.link_libc) @as(posix.E, @enumFromInt(rc)) else posix.errno(rc)) {10981 switch (if (builtin.link_libc) @as(posix.E, @enumFromInt(rc)) else posix.errno(rc)) {
10947 .SUCCESS => {
10948 syscall.finish();
10949 return;
10950 },
10951 .INTR => {10982 .INTR => {
10952 try syscall.checkCancel();10983 try syscall.checkCancel();
10953 continue;10984 continue;
10954 },10985 },
10955 .INVAL => return syscall.fail(error.UnsupportedClock),10986 // Handles SUCCESS as well as clock not available and unexpected
10956 else => |err| return syscall.unexpectedErrno(err),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}
1096010997
10961fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void {10998fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void {
10962 const t_io = ioBasic(t);10999 const t_io = ioBasic(t);
10963 const w = std.os.wasi;11000 const w = std.os.wasi;
1096411001
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 .id = clockToWasi(d.clock),11003 .id = clockToWasi(d.clock),
10967 .timeout = std.math.lossyCast(u64, d.raw.nanoseconds),11004 .timeout = std.math.lossyCast(u64, d.raw.nanoseconds),
10968 .precision = 0,11005 .precision = 0,
...@@ -10987,13 +11024,13 @@ fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void {...@@ -10987,13 +11024,13 @@ fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void {
10987 syscall.finish();11024 syscall.finish();
10988}11025}
1098911026
10990fn sleepNanosleep(t: *Threaded, timeout: Io.Timeout) Io.SleepError!void {11027fn sleepNanosleep(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void {
10991 const t_io = ioBasic(t);11028 const t_io = ioBasic(t);
10992 const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;11029 const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;
10993 const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;11030 const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;
1099411031
10995 var timespec: posix.timespec = t: {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 .sec = std.math.maxInt(sec_type),11034 .sec = std.math.maxInt(sec_type),
10998 .nsec = std.math.maxInt(nsec_type),11035 .nsec = std.math.maxInt(nsec_type),
10999 };11036 };
...@@ -12630,7 +12667,7 @@ fn netReceivePosix(...@@ -12630,7 +12667,7 @@ fn netReceivePosix(
12630 var message_i: usize = 0;12667 var message_i: usize = 0;
12631 var data_i: usize = 0;12668 var data_i: usize = 0;
1263212669
12633 const deadline = timeout.toTimestamp(t_io) catch |err| return .{ err, message_i };12670 const deadline = timeout.toTimestamp(t_io);
1263412671
12635 recv: while (true) {12672 recv: while (true) {
12636 if (message_buffer.len - message_i == 0) return .{ null, message_i };12673 if (message_buffer.len - message_i == 0) return .{ null, message_i };
...@@ -12678,7 +12715,7 @@ fn netReceivePosix(...@@ -12678,7 +12715,7 @@ fn netReceivePosix(
1267812715
12679 const max_poll_ms = std.math.maxInt(u31);12716 const max_poll_ms = std.math.maxInt(u31);
12680 const timeout_ms: u31 = if (deadline) |d| t: {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 if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i };12719 if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i };
12683 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));12720 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));
12684 } else max_poll_ms;12721 } else max_poll_ms;
...@@ -13875,7 +13912,11 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) File.Stat {...@@ -13875,7 +13912,11 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) File.Stat {
13875}13912}
1387613913
13877fn timestampFromPosix(timespec: *const posix.timespec) Io.Timestamp {13914fn 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
13918fn nanosecondsFromPosix(timespec: *const posix.timespec) i96 {
13919 return @intCast(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec);
13879}13920}
1388013921
13881fn timestampToPosix(nanoseconds: i96) posix.timespec {13922fn timestampToPosix(nanoseconds: i96) posix.timespec {
...@@ -14013,13 +14054,13 @@ fn lookupDns(...@@ -14013,13 +14054,13 @@ fn lookupDns(
14013 // boot clock is chosen because time the computer is suspended should count14054 // boot clock is chosen because time the computer is suspended should count
14014 // against time spent waiting for external messages to arrive.14055 // against time spent waiting for external messages to arrive.
14015 const clock: Io.Clock = .boot;14056 const clock: Io.Clock = .boot;
14016 var now_ts = try clock.now(t_io);14057 var now_ts = clock.now(t_io);
14017 const final_ts = now_ts.addDuration(.fromSeconds(rc.timeout_seconds));14058 const final_ts = now_ts.addDuration(.fromSeconds(rc.timeout_seconds));
14018 const attempt_duration: Io.Duration = .{14059 const attempt_duration: Io.Duration = .{
14019 .nanoseconds = (std.time.ns_per_s / rc.attempts) * @as(i96, rc.timeout_seconds),14060 .nanoseconds = (std.time.ns_per_s / rc.attempts) * @as(i96, rc.timeout_seconds),
14020 };14061 };
1402114062
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 const max_messages = queries_buffer.len * HostName.ResolvConf.max_nameservers;14064 const max_messages = queries_buffer.len * HostName.ResolvConf.max_nameservers;
14024 {14065 {
14025 var message_buffer: [max_messages]Io.net.OutgoingMessage = undefined;14066 var message_buffer: [max_messages]Io.net.OutgoingMessage = undefined;
...@@ -17021,7 +17062,7 @@ const parking_futex = struct {...@@ -17021,7 +17062,7 @@ const parking_futex = struct {
17021 const deadline: ?Io.Clock.Timestamp = switch (timeout) {17062 const deadline: ?Io.Clock.Timestamp = switch (timeout) {
17022 .none => null,17063 .none => null,
17023 .duration => |d| .{17064 .duration => |d| .{
17024 .raw = (nowInner(d.clock) catch unreachable).addDuration(d.raw),17065 .raw = nowInner(d.clock).addDuration(d.raw),
17025 .clock = d.clock,17066 .clock = d.clock,
17026 },17067 },
17027 .deadline => |d| d,17068 .deadline => |d| d,
...@@ -17143,7 +17184,7 @@ const parking_sleep = struct {...@@ -17143,7 +17184,7 @@ const parking_sleep = struct {
17143 comptime {17184 comptime {
17144 assert(use_parking_sleep);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 const opt_thread = Thread.current;17188 const opt_thread = Thread.current;
17148 cancelable: {17189 cancelable: {
17149 const thread = opt_thread orelse break :cancelable;17190 const thread = opt_thread orelse break :cancelable;
...@@ -17216,12 +17257,9 @@ const parking_sleep = struct {...@@ -17216,12 +17257,9 @@ const parking_sleep = struct {
17216 }17257 }
17217 /// Sleep for approximately `ms` awake milliseconds in an attempt to work around Windows kernel bugs.17258 /// Sleep for approximately `ms` awake milliseconds in an attempt to work around Windows kernel bugs.
17218 fn windowsRetrySleep(ms: u32) (Io.Cancelable || Io.UnexpectedError)!void {17259 fn windowsRetrySleep(ms: u32) (Io.Cancelable || Io.UnexpectedError)!void {
17219 const now_timestamp = nowWindows(.awake) catch unreachable; // '.awake' is supported on Windows17260 const now_timestamp = nowWindows(.awake); // '.awake' is supported on Windows
17220 const deadline = now_timestamp.addDuration(.fromMilliseconds(ms));17261 const deadline = now_timestamp.addDuration(.fromMilliseconds(ms));
17221 parking_sleep.sleep(.{ .raw = deadline, .clock = .awake }) catch |err| switch (err) {17262 try parking_sleep.sleep(.{ .raw = deadline, .clock = .awake });
17222 error.UnsupportedClock => unreachable,
17223 else => |e| return e,
17224 };
17225 }17263 }
17226};17264};
1722717265
...@@ -17234,7 +17272,7 @@ fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{T...@@ -17234,7 +17272,7 @@ fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{T
17234 .windows => {17272 .windows => {
17235 var timeout_buf: windows.LARGE_INTEGER = undefined;17273 var timeout_buf: windows.LARGE_INTEGER = undefined;
17236 const raw_timeout: ?*windows.LARGE_INTEGER = if (opt_deadline) |deadline| timeout: {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 const nanoseconds = now_timestamp.durationTo(deadline.raw).nanoseconds;17276 const nanoseconds = now_timestamp.durationTo(deadline.raw).nanoseconds;
17239 timeout_buf = @intCast(@divTrunc(-nanoseconds, 100));17277 timeout_buf = @intCast(@divTrunc(-nanoseconds, 100));
17240 break :timeout &timeout_buf;17278 break :timeout &timeout_buf;
...@@ -17284,17 +17322,17 @@ fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{T...@@ -17284,17 +17322,17 @@ fn park(opt_deadline: ?Io.Clock.Timestamp, addr_hint: ?*const anyopaque) error{T
17284 }17322 }
17285}17323}
1728617324
17287fn deadlineToWindowsInterval(t: *Io.Threaded, deadline: Io.Clock.Timestamp) Io.Clock.Error!windows.LARGE_INTEGER {17325fn deadlineToWindowsInterval(t: *Io.Threaded, deadline: Io.Clock.Timestamp) windows.LARGE_INTEGER {
17288 // ntdll only supports two combinations:17326 // ntdll only supports two combinations:
17289 // * real-time (`.real`) sleeps with absolute deadlines17327 // * real-time (`.real`) sleeps with absolute deadlines
17290 // * monotonic (`.awake`/`.boot`) sleeps with relative durations17328 // * monotonic (`.awake`/`.boot`) sleeps with relative durations
17291 switch (deadline.clock) {17329 switch (deadline.clock) {
17292 .cpu_process, .cpu_thread => unreachable, // cannot sleep for CPU time17330 .cpu_process, .cpu_thread => return 0,
17293 .real => {17331 .real => {
17294 return @intCast(@max(@divTrunc(deadline.raw.nanoseconds, 100), 0));17332 return @intCast(@max(@divTrunc(deadline.raw.nanoseconds, 100), 0));
17295 },17333 },
17296 .awake, .boot => {17334 .awake, .boot => {
17297 const duration = try deadline.durationFromNow(ioBasic(t));17335 const duration = deadline.durationFromNow(ioBasic(t));
17298 return @intCast(@min(@divTrunc(-duration.raw.nanoseconds, 100), -1));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,7 +1137,7 @@ pub const Socket = struct {
1137 const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, .none);1137 const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, .none);
1138 if (maybe_err) |err| switch (err) {1138 if (maybe_err) |err| switch (err) {
1139 // No timeout is passed to `netReceieve`, so it must not return timeout related errors.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 else => |e| return e,1141 else => |e| return e,
1142 };1142 };
1143 assert(1 == count);1143 assert(1 == count);
lib/std/Io/net/HostName.zig+1-1
...@@ -145,7 +145,7 @@ pub const LookupError = error{...@@ -145,7 +145,7 @@ pub const LookupError = error{
145 NoAddressReturned,145 NoAddressReturned,
146 /// Failed to open or read "/etc/hosts" or "/etc/resolv.conf".146 /// Failed to open or read "/etc/hosts" or "/etc/resolv.conf".
147 DetectingNetworkConfigurationFailed,147 DetectingNetworkConfigurationFailed,
148} || Io.Clock.Error || IpAddress.BindError || Io.Cancelable;148} || IpAddress.BindError || Io.Cancelable;
149149
150pub const LookupResult = union(enum) {150pub const LookupResult = union(enum) {
151 address: IpAddress,151 address: IpAddress,
lib/std/Io/test.zig-6
...@@ -216,14 +216,12 @@ test "Group.cancel" {...@@ -216,14 +216,12 @@ test "Group.cancel" {
216 defer result.* = 1;216 defer result.* = 1;
217 io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {217 io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {
218 error.Canceled => |e| return e,218 error.Canceled => |e| return e,
219 else => {},
220 };219 };
221 }220 }
222221
223 fn sleepRecancel(io: Io, result: *usize) void {222 fn sleepRecancel(io: Io, result: *usize) void {
224 io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {223 io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {
225 error.Canceled => io.recancel(),224 error.Canceled => io.recancel(),
226 else => {},
227 };225 };
228 result.* = 1;226 result.* = 1;
229 }227 }
...@@ -523,8 +521,6 @@ test "cancel sleep" {...@@ -523,8 +521,6 @@ test "cancel sleep" {
523 fn blockUntilCanceled(io: Io) void {521 fn blockUntilCanceled(io: Io) void {
524 while (true) io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {522 while (true) io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {
525 error.Canceled => return,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,8 +548,6 @@ test "tasks spawned in group after Group.cancel are canceled" {
552 fn blockUntilCanceled(io: Io) Io.Cancelable!void {548 fn blockUntilCanceled(io: Io) Io.Cancelable!void {
553 while (true) io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {549 while (true) io.sleep(.fromSeconds(100_000), .awake) catch |err| switch (err) {
554 error.Canceled => |e| return e,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,7 +212,7 @@ pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, io: Io, now: Io.Timestamp, i
212 }212 }
213}213}
214214
215pub const AddCertsFromFilePathError = Io.File.OpenError || AddCertsFromFileError || Io.Clock.Error;215pub const AddCertsFromFilePathError = Io.File.OpenError || AddCertsFromFileError;
216216
217pub fn addCertsFromFilePathAbsolute(217pub fn addCertsFromFilePathAbsolute(
218 cb: *Bundle,218 cb: *Bundle,
...@@ -338,7 +338,7 @@ test "scan for OS-provided certificates" {...@@ -338,7 +338,7 @@ test "scan for OS-provided certificates" {
338 var bundle: Bundle = .{};338 var bundle: Bundle = .{};
339 defer bundle.deinit(gpa);339 defer bundle.deinit(gpa);
340340
341 const now = try Io.Clock.real.now(io);341 const now = Io.Clock.real.now(io);
342342
343 try bundle.rescan(gpa, io, now);343 try bundle.rescan(gpa, io, now);
344}344}
lib/std/http/Client.zig+1-1
...@@ -1700,7 +1700,7 @@ pub fn request(...@@ -1700,7 +1700,7 @@ pub fn request(
1700 defer client.ca_bundle_mutex.unlock(io);1700 defer client.ca_bundle_mutex.unlock(io);
17011701
1702 if (client.now == null) {1702 if (client.now == null) {
1703 const now = try Io.Clock.real.now(io);1703 const now = Io.Clock.real.now(io);
1704 client.now = now;1704 client.now = now;
1705 client.ca_bundle.rescan(client.allocator, io, now) catch1705 client.ca_bundle.rescan(client.allocator, io, now) catch
1706 return error.CertificateBundleLoadFailure;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,21 +1914,21 @@ fn init_vdso_clock_gettime(clk: clockid_t, ts: *timespec) callconv(.c) usize {
1914 @atomicStore(?VdsoClockGettime, &vdso_clock_gettime, ptr, .monotonic);1914 @atomicStore(?VdsoClockGettime, &vdso_clock_gettime, ptr, .monotonic);
1915 // Call into the VDSO if available1915 // Call into the VDSO if available
1916 if (ptr) |f| return f(clk, ts);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}
19191919
1920pub fn clock_getres(clk_id: i32, tp: *timespec) usize {1920pub fn clock_getres(clk_id: clockid_t, tp: *timespec) usize {
1921 return syscall2(1921 return syscall2(
1922 if (@hasField(SYS, "clock_getres") and native_arch != .hexagon) .clock_getres else .clock_getres_time64,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 @intFromPtr(tp),1924 @intFromPtr(tp),
1925 );1925 );
1926}1926}
19271927
1928pub fn clock_settime(clk_id: i32, tp: *const timespec) usize {1928pub fn clock_settime(clk_id: clockid_t, tp: *const timespec) usize {
1929 return syscall2(1929 return syscall2(
1930 if (@hasField(SYS, "clock_settime") and native_arch != .hexagon) .clock_settime else .clock_settime64,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 @intFromPtr(tp),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,12 +620,12 @@ test "timeout (after a relative time)" {
620 const margin = 5;620 const margin = 5;
621 const ts: linux.kernel_timespec = .{ .sec = 0, .nsec = ms * 1000000 };621 const ts: linux.kernel_timespec = .{ .sec = 0, .nsec = ms * 1000000 };
622622
623 const started = try std.Io.Clock.awake.now(io);623 const started = std.Io.Clock.awake.now(io);
624 const sqe = try ring.timeout(0x55555555, &ts, 0, 0);624 const sqe = try ring.timeout(0x55555555, &ts, 0, 0);
625 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe.opcode);625 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe.opcode);
626 try testing.expectEqual(@as(u32, 1), try ring.submit());626 try testing.expectEqual(@as(u32, 1), try ring.submit());
627 const cqe = try ring.copy_cqe();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);
629629
630 try testing.expectEqual(linux.io_uring_cqe{630 try testing.expectEqual(linux.io_uring_cqe{
631 .user_data = 0x55555555,631 .user_data = 0x55555555,
lib/std/posix.zig-52
...@@ -864,58 +864,6 @@ pub fn dl_iterate_phdr(...@@ -864,58 +864,6 @@ pub fn dl_iterate_phdr(
864 }864 }
865}865}
866866
867pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
868
869pub 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
897pub 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
919pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;867pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
920868
921pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {869pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
lib/std/time.zig-182
...@@ -1,11 +1,3 @@...@@ -1,11 +1,3 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const testing = std.testing;
5const math = std.math;
6const windows = std.os.windows;
7const posix = std.posix;
8
9pub const epoch = @import("time/epoch.zig");1pub const epoch = @import("time/epoch.zig");
102
11// Divisions of a nanosecond.3// Divisions of a nanosecond.
...@@ -38,180 +30,6 @@ pub const s_per_hour = s_per_min * 60;...@@ -38,180 +30,6 @@ pub const s_per_hour = s_per_min * 60;
38pub const s_per_day = s_per_hour * 24;30pub const s_per_day = s_per_hour * 24;
39pub const s_per_week = s_per_day * 7;31pub const s_per_week = s_per_day * 7;
4032
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.
48pub 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.
158pub 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
202test 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
215test {33test {
216 _ = epoch;34 _ = epoch;
217}35}
src/Compilation.zig+15-20
...@@ -331,48 +331,42 @@ const QueuedJobs = struct {...@@ -331,48 +331,42 @@ const QueuedJobs = struct {
331pub const Timer = union(enum) {331pub const Timer = union(enum) {
332 unused,332 unused,
333 active: struct {333 active: struct {
334 start: std.time.Instant,334 start: Io.Timestamp,
335 saved_ns: u64,335 saved_ns: u64,
336 },336 },
337 paused: u64,337 paused: u64,
338 stopped,338 stopped,
339339
340 pub fn pause(t: *Timer) void {340 pub fn pause(t: *Timer, io: Io) void {
341 switch (t.*) {341 switch (t.*) {
342 .unused => return,342 .unused => return,
343 .active => |a| {343 .active => |a| {
344 const current = std.time.Instant.now() catch unreachable;344 const current: Io.Timestamp = .now(io, .awake);
345 const new_ns = switch (current.order(a.start)) {345 const new_ns: u64 = @intCast(current.nanoseconds -| a.start.nanoseconds);
346 .lt, .eq => 0,
347 .gt => current.since(a.start),
348 };
349 t.* = .{ .paused = a.saved_ns + new_ns };346 t.* = .{ .paused = a.saved_ns + new_ns };
350 },347 },
351 .paused => unreachable,348 .paused => unreachable,
352 .stopped => unreachable,349 .stopped => unreachable,
353 }350 }
354 }351 }
355 pub fn @"resume"(t: *Timer) void {352 pub fn @"resume"(t: *Timer, io: Io) void {
356 switch (t.*) {353 switch (t.*) {
357 .unused => return,354 .unused => return,
358 .active => unreachable,355 .active => unreachable,
359 .paused => |saved_ns| t.* = .{ .active = .{356 .paused => |saved_ns| t.* = .{ .active = .{
360 .start = std.time.Instant.now() catch unreachable,357 .start = .now(io, .awake),
361 .saved_ns = saved_ns,358 .saved_ns = saved_ns,
362 } },359 } },
363 .stopped => unreachable,360 .stopped => unreachable,
364 }361 }
365 }362 }
366 pub fn finish(t: *Timer) ?u64 {363 pub fn finish(t: *Timer, io: Io) ?u64 {
367 defer t.* = .stopped;364 defer t.* = .stopped;
368 switch (t.*) {365 switch (t.*) {
369 .unused => return null,366 .unused => return null,
370 .active => |a| {367 .active => |a| {
371 const current = std.time.Instant.now() catch unreachable;368 const current: Io.Timestamp = .now(io, .awake);
372 const new_ns = switch (current.order(a.start)) {369 const new_ns: u64 = @intCast(current.nanoseconds -| a.start.nanoseconds);
373 .lt, .eq => 0,
374 .gt => current.since(a.start),
375 };
376 return a.saved_ns + new_ns;370 return a.saved_ns + new_ns;
377 },371 },
378 .paused => |ns| return ns,372 .paused => |ns| return ns,
...@@ -387,7 +381,8 @@ pub const Timer = union(enum) {...@@ -387,7 +381,8 @@ pub const Timer = union(enum) {
387/// is set.381/// is set.
388pub fn startTimer(comp: *Compilation) Timer {382pub fn startTimer(comp: *Compilation) Timer {
389 if (comp.time_report == null) return .unused;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 return .{ .active = .{386 return .{ .active = .{
392 .start = now,387 .start = now,
393 .saved_ns = 0,388 .saved_ns = 0,
...@@ -3408,7 +3403,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel...@@ -3408,7 +3403,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
3408 defer sub_prog_node.end();3403 defer sub_prog_node.end();
34093404
3410 var timer = comp.startTimer();3405 var timer = comp.startTimer();
3411 defer if (timer.finish()) |ns| {3406 defer if (timer.finish(io)) |ns| {
3412 comp.mutex.lockUncancelable(io);3407 comp.mutex.lockUncancelable(io);
3413 defer comp.mutex.unlock(io);3408 defer comp.mutex.unlock(io);
3414 comp.time_report.?.stats.real_ns_llvm_emit = ns;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,7 +3448,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
3453 }3448 }
3454 if (comp.bin_file) |lf| {3449 if (comp.bin_file) |lf| {
3455 var timer = comp.startTimer();3450 var timer = comp.startTimer();
3456 defer if (timer.finish()) |ns| {3451 defer if (timer.finish(io)) |ns| {
3457 comp.mutex.lockUncancelable(io);3452 comp.mutex.lockUncancelable(io);
3458 defer comp.mutex.unlock(io);3453 defer comp.mutex.unlock(io);
3459 comp.time_report.?.stats.real_ns_link_flush = ns;3454 comp.time_report.?.stats.real_ns_link_flush = ns;
...@@ -4686,7 +4681,7 @@ fn performAllTheWork(...@@ -4686,7 +4681,7 @@ fn performAllTheWork(
4686 var decl_work_timer: ?Timer = null;4681 var decl_work_timer: ?Timer = null;
4687 defer commit_timer: {4682 defer commit_timer: {
4688 const t = &(decl_work_timer orelse break :commit_timer);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 comp.mutex.lockUncancelable(io);4685 comp.mutex.lockUncancelable(io);
4691 defer comp.mutex.unlock(io);4686 defer comp.mutex.unlock(io);
4692 comp.time_report.?.stats.real_ns_decls = ns;4687 comp.time_report.?.stats.real_ns_decls = ns;
...@@ -4719,7 +4714,7 @@ fn performAllTheWork(...@@ -4719,7 +4714,7 @@ fn performAllTheWork(
4719 defer zir_prog_node.end();4714 defer zir_prog_node.end();
47204715
4721 var timer = comp.startTimer();4716 var timer = comp.startTimer();
4722 defer if (timer.finish()) |ns| {4717 defer if (timer.finish(io)) |ns| {
4723 comp.mutex.lockUncancelable(io);4718 comp.mutex.lockUncancelable(io);
4724 defer comp.mutex.unlock(io);4719 defer comp.mutex.unlock(io);
4725 comp.time_report.?.stats.real_ns_files = ns;4720 comp.time_report.?.stats.real_ns_files = ns;
src/Zcu.zig+6-4
...@@ -4754,6 +4754,7 @@ const TrackedUnitSema = struct {...@@ -4754,6 +4754,7 @@ const TrackedUnitSema = struct {
4754 analysis_timer_decl: ?InternPool.TrackedInst.Index,4754 analysis_timer_decl: ?InternPool.TrackedInst.Index,
4755 pub fn end(tus: TrackedUnitSema, zcu: *Zcu) void {4755 pub fn end(tus: TrackedUnitSema, zcu: *Zcu) void {
4756 const comp = zcu.comp;4756 const comp = zcu.comp;
4757 const io = comp.io;
4757 if (tus.old_name) |old_name| {4758 if (tus.old_name) |old_name| {
4758 zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion4759 zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion
4759 zcu.cur_sema_prog_node.setName(&old_name);4760 zcu.cur_sema_prog_node.setName(&old_name);
...@@ -4762,9 +4763,8 @@ const TrackedUnitSema = struct {...@@ -4762,9 +4763,8 @@ const TrackedUnitSema = struct {
4762 zcu.cur_sema_prog_node = .none;4763 zcu.cur_sema_prog_node = .none;
4763 }4764 }
4764 report_time: {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 const zir_decl = tus.analysis_timer_decl orelse break :report_time;4767 const zir_decl = tus.analysis_timer_decl orelse break :report_time;
4767 const io = comp.io;
4768 comp.mutex.lockUncancelable(io);4768 comp.mutex.lockUncancelable(io);
4769 defer comp.mutex.unlock(io);4769 defer comp.mutex.unlock(io);
4770 comp.time_report.?.stats.cpu_ns_sema += sema_ns;4770 comp.time_report.?.stats.cpu_ns_sema += sema_ns;
...@@ -4779,11 +4779,13 @@ const TrackedUnitSema = struct {...@@ -4779,11 +4779,13 @@ const TrackedUnitSema = struct {
4779 gop.value_ptr.count += 1;4779 gop.value_ptr.count += 1;
4780 }4780 }
4781 zcu.cur_analysis_timer = tus.old_analysis_timer;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};
4785pub fn trackUnitSema(zcu: *Zcu, name: []const u8, zir_inst: ?InternPool.TrackedInst.Index) TrackedUnitSema {4785pub 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 const old_analysis_timer = zcu.cur_analysis_timer;4789 const old_analysis_timer = zcu.cur_analysis_timer;
4788 zcu.cur_analysis_timer = zcu.comp.startTimer();4790 zcu.cur_analysis_timer = zcu.comp.startTimer();
4789 const old_name: ?[std.Progress.Node.max_name_len]u8 = old_name: {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,7 +263,7 @@ pub fn updateFile(
263 var timer = comp.startTimer();263 var timer = comp.startTimer();
264 // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen.264 // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen.
265 file.tree = try Ast.parse(gpa, source, file.getMode());265 file.tree = try Ast.parse(gpa, source, file.getMode());
266 if (timer.finish()) |ns_parse| {266 if (timer.finish(io)) |ns_parse| {
267 comp.mutex.lockUncancelable(io);267 comp.mutex.lockUncancelable(io);
268 defer comp.mutex.unlock(io);268 defer comp.mutex.unlock(io);
269 comp.time_report.?.stats.cpu_ns_parse += ns_parse;269 comp.time_report.?.stats.cpu_ns_parse += ns_parse;
...@@ -295,7 +295,7 @@ pub fn updateFile(...@@ -295,7 +295,7 @@ pub fn updateFile(
295 else => |e| return e,295 else => |e| return e,
296 };296 };
297297
298 if (timer.finish()) |ns_astgen| {298 if (timer.finish(io)) |ns_astgen| {
299 comp.mutex.lockUncancelable(io);299 comp.mutex.lockUncancelable(io);
300 defer comp.mutex.unlock(io);300 defer comp.mutex.unlock(io);
301 comp.time_report.?.stats.cpu_ns_astgen += ns_astgen;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,7 +4485,7 @@ pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) Ru
44854485
4486 const codegen_result = runCodegenInner(pt, func_index, air);4486 const codegen_result = runCodegenInner(pt, func_index, air);
44874487
4488 if (timer.finish()) |ns_codegen| report_time: {4488 if (timer.finish(io)) |ns_codegen| report_time: {
4489 const ip = &zcu.intern_pool;4489 const ip = &zcu.intern_pool;
4490 const nav = ip.indexToKey(func_index).func.owner_nav;4490 const nav = ip.indexToKey(func_index).func.owner_nav;
4491 const zir_decl = ip.getNav(nav).srcInst(ip);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,7 +1388,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1388 };1388 };
13891389
1390 var timer = comp.startTimer();1390 var timer = comp.startTimer();
1391 defer if (timer.finish()) |ns| {1391 defer if (timer.finish(io)) |ns| {
1392 comp.mutex.lockUncancelable(io);1392 comp.mutex.lockUncancelable(io);
1393 defer comp.mutex.unlock(io);1393 defer comp.mutex.unlock(io);
1394 comp.time_report.?.stats.cpu_ns_link += ns;1394 comp.time_report.?.stats.cpu_ns_link += ns;
...@@ -1535,12 +1535,12 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {...@@ -1535,12 +1535,12 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
1535 break :nav nav_index;1535 break :nav nav_index;
1536 },1536 },
1537 .link_func => |codegen_task| nav: {1537 .link_func => |codegen_task| nav: {
1538 timer.pause();1538 timer.pause(io);
1539 const func, var mir = codegen_task.wait(&zcu.codegen_task_pool, io) catch |err| switch (err) {1539 const func, var mir = codegen_task.wait(&zcu.codegen_task_pool, io) catch |err| switch (err) {
1540 error.Canceled, error.AlreadyReported => return,1540 error.Canceled, error.AlreadyReported => return,
1541 };1541 };
1542 defer mir.deinit(zcu);1542 defer mir.deinit(zcu);
1543 timer.@"resume"();1543 timer.@"resume"(io);
15441544
1545 const nav = zcu.funcInfo(func).owner_nav;1545 const nav = zcu.funcInfo(func).owner_nav;
1546 const fqn_slice = ip.getNav(nav).fqn.toSlice(ip);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,7 +1592,7 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
1592 },1592 },
1593 };1593 };
15941594
1595 if (timer.finish()) |ns_link| report_time: {1595 if (timer.finish(io)) |ns_link| report_time: {
1596 comp.mutex.lockUncancelable(io);1596 comp.mutex.lockUncancelable(io);
1597 defer comp.mutex.unlock(io);1597 defer comp.mutex.unlock(io);
1598 const tr = &zcu.comp.time_report.?;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,6 +924,15 @@ uint32_t wasi_snapshot_preview1_clock_time_get(uint32_t id, uint64_t precision,
924 return wasi_errno_success;924 return wasi_errno_success;
925}925}
926926
927uint32_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
927uint32_t wasi_snapshot_preview1_path_remove_directory(uint32_t fd, uint32_t path, uint32_t path_len) {936uint32_t wasi_snapshot_preview1_path_remove_directory(uint32_t fd, uint32_t path, uint32_t path_len) {
928 uint8_t *const m = *wasm_memory;937 uint8_t *const m = *wasm_memory;
929 const char *path_ptr = (const char *)&m[path];938 const char *path_ptr = (const char *)&m[path];