authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-08 21:56:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:49-07:00
logebcc6f166c9c34d00b750f26687c9ee36b243cb0
tree73ba685c9985284f2ebf5aee3b81fb30eaca9c86
parent89412fda775aecdedf4047355f2c45b48334a285

std.Io: bring back Timestamp but also keep Clock.Timestamp

this feels better

11 files changed, 233 insertions(+), 177 deletions(-)

lib/compiler/build_runner.zig+1-1
...@@ -556,7 +556,7 @@ pub fn main() !void {...@@ -556,7 +556,7 @@ pub fn main() !void {
556 try run.thread_pool.init(thread_pool_options);556 try run.thread_pool.init(thread_pool_options);
557 defer run.thread_pool.deinit();557 defer run.thread_pool.deinit();
558558
559 const now = Io.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err});559 const now = Io.Clock.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err});
560560
561 run.web_server = if (webui_listen) |listen_address| ws: {561 run.web_server = if (webui_listen) |listen_address| ws: {
562 if (builtin.single_threaded) unreachable; // `fatal` above562 if (builtin.single_threaded) unreachable; // `fatal` above
lib/std/Build/Cache.zig+13-13
...@@ -21,7 +21,7 @@ io: Io,...@@ -21,7 +21,7 @@ io: Io,
21manifest_dir: fs.Dir,21manifest_dir: fs.Dir,
22hash: HashHelper = .{},22hash: HashHelper = .{},
23/// This value is accessed from multiple threads, protected by mutex.23/// This value is accessed from multiple threads, protected by mutex.
24recent_problematic_timestamp: i128 = 0,24recent_problematic_timestamp: Io.Timestamp = .zero,
25mutex: std.Thread.Mutex = .{},25mutex: std.Thread.Mutex = .{},
2626
27/// A set of strings such as the zig library directory or project source root, which27/// A set of strings such as the zig library directory or project source root, which
...@@ -155,7 +155,7 @@ pub const File = struct {...@@ -155,7 +155,7 @@ pub const File = struct {
155 pub const Stat = struct {155 pub const Stat = struct {
156 inode: fs.File.INode,156 inode: fs.File.INode,
157 size: u64,157 size: u64,
158 mtime: i128,158 mtime: Io.Timestamp,
159159
160 pub fn fromFs(fs_stat: fs.File.Stat) Stat {160 pub fn fromFs(fs_stat: fs.File.Stat) Stat {
161 return .{161 return .{
...@@ -330,7 +330,7 @@ pub const Manifest = struct {...@@ -330,7 +330,7 @@ pub const Manifest = struct {
330 diagnostic: Diagnostic = .none,330 diagnostic: Diagnostic = .none,
331 /// Keeps track of the last time we performed a file system write to observe331 /// Keeps track of the last time we performed a file system write to observe
332 /// what time the file system thinks it is, according to its own granularity.332 /// what time the file system thinks it is, according to its own granularity.
333 recent_problematic_timestamp: i128 = 0,333 recent_problematic_timestamp: Io.Timestamp = .zero,
334334
335 pub const Diagnostic = union(enum) {335 pub const Diagnostic = union(enum) {
336 none,336 none,
...@@ -728,7 +728,7 @@ pub const Manifest = struct {...@@ -728,7 +728,7 @@ pub const Manifest = struct {
728 file.stat = .{728 file.stat = .{
729 .size = stat_size,729 .size = stat_size,
730 .inode = stat_inode,730 .inode = stat_inode,
731 .mtime = stat_mtime,731 .mtime = .{ .nanoseconds = stat_mtime },
732 };732 };
733 file.bin_digest = file_bin_digest;733 file.bin_digest = file_bin_digest;
734 break :f file;734 break :f file;
...@@ -747,7 +747,7 @@ pub const Manifest = struct {...@@ -747,7 +747,7 @@ pub const Manifest = struct {
747 .stat = .{747 .stat = .{
748 .size = stat_size,748 .size = stat_size,
749 .inode = stat_inode,749 .inode = stat_inode,
750 .mtime = stat_mtime,750 .mtime = .{ .nanoseconds = stat_mtime },
751 },751 },
752 .bin_digest = file_bin_digest,752 .bin_digest = file_bin_digest,
753 };753 };
...@@ -780,7 +780,7 @@ pub const Manifest = struct {...@@ -780,7 +780,7 @@ pub const Manifest = struct {
780 return error.CacheCheckFailed;780 return error.CacheCheckFailed;
781 };781 };
782 const size_match = actual_stat.size == cache_hash_file.stat.size;782 const size_match = actual_stat.size == cache_hash_file.stat.size;
783 const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime;783 const mtime_match = actual_stat.mtime.nanoseconds == cache_hash_file.stat.mtime.nanoseconds;
784 const inode_match = actual_stat.inode == cache_hash_file.stat.inode;784 const inode_match = actual_stat.inode == cache_hash_file.stat.inode;
785785
786 if (!size_match or !mtime_match or !inode_match) {786 if (!size_match or !mtime_match or !inode_match) {
...@@ -792,7 +792,7 @@ pub const Manifest = struct {...@@ -792,7 +792,7 @@ pub const Manifest = struct {
792792
793 if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {793 if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) {
794 // The actual file has an unreliable timestamp, force it to be hashed794 // The actual file has an unreliable timestamp, force it to be hashed
795 cache_hash_file.stat.mtime = 0;795 cache_hash_file.stat.mtime = .zero;
796 cache_hash_file.stat.inode = 0;796 cache_hash_file.stat.inode = 0;
797 }797 }
798798
...@@ -848,10 +848,10 @@ pub const Manifest = struct {...@@ -848,10 +848,10 @@ pub const Manifest = struct {
848 }848 }
849 }849 }
850850
851 fn isProblematicTimestamp(man: *Manifest, file_time: i128) bool {851 fn isProblematicTimestamp(man: *Manifest, timestamp: Io.Timestamp) bool {
852 // If the file_time is prior to the most recent problematic timestamp852 // If the file_time is prior to the most recent problematic timestamp
853 // then we don't need to access the filesystem.853 // then we don't need to access the filesystem.
854 if (file_time < man.recent_problematic_timestamp)854 if (timestamp.nanoseconds < man.recent_problematic_timestamp.nanoseconds)
855 return false;855 return false;
856856
857 // Next we will check the globally shared Cache timestamp, which is accessed857 // Next we will check the globally shared Cache timestamp, which is accessed
...@@ -861,7 +861,7 @@ pub const Manifest = struct {...@@ -861,7 +861,7 @@ pub const Manifest = struct {
861861
862 // Save the global one to our local one to avoid locking next time.862 // Save the global one to our local one to avoid locking next time.
863 man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp;863 man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp;
864 if (file_time < man.recent_problematic_timestamp)864 if (timestamp.nanoseconds < man.recent_problematic_timestamp.nanoseconds)
865 return false;865 return false;
866866
867 // This flag prevents multiple filesystem writes for the same hit() call.867 // This flag prevents multiple filesystem writes for the same hit() call.
...@@ -879,7 +879,7 @@ pub const Manifest = struct {...@@ -879,7 +879,7 @@ pub const Manifest = struct {
879 man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp;879 man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp;
880 }880 }
881881
882 return file_time >= man.recent_problematic_timestamp;882 return timestamp.nanoseconds >= man.recent_problematic_timestamp.nanoseconds;
883 }883 }
884884
885 fn populateFileHash(self: *Manifest, ch_file: *File) !void {885 fn populateFileHash(self: *Manifest, ch_file: *File) !void {
...@@ -904,7 +904,7 @@ pub const Manifest = struct {...@@ -904,7 +904,7 @@ pub const Manifest = struct {
904904
905 if (self.isProblematicTimestamp(ch_file.stat.mtime)) {905 if (self.isProblematicTimestamp(ch_file.stat.mtime)) {
906 // The actual file has an unreliable timestamp, force it to be hashed906 // The actual file has an unreliable timestamp, force it to be hashed
907 ch_file.stat.mtime = 0;907 ch_file.stat.mtime = .zero;
908 ch_file.stat.inode = 0;908 ch_file.stat.inode = 0;
909 }909 }
910910
...@@ -1040,7 +1040,7 @@ pub const Manifest = struct {...@@ -1040,7 +1040,7 @@ pub const Manifest = struct {
10401040
1041 if (self.isProblematicTimestamp(new_file.stat.mtime)) {1041 if (self.isProblematicTimestamp(new_file.stat.mtime)) {
1042 // The actual file has an unreliable timestamp, force it to be hashed1042 // The actual file has an unreliable timestamp, force it to be hashed
1043 new_file.stat.mtime = 0;1043 new_file.stat.mtime = .zero;
1044 new_file.stat.inode = 0;1044 new_file.stat.inode = 0;
1045 }1045 }
10461046
lib/std/Build/WebServer.zig+9-8
...@@ -11,7 +11,7 @@ tcp_server: ?net.Server,...@@ -11,7 +11,7 @@ tcp_server: ?net.Server,
11serve_thread: ?std.Thread,11serve_thread: ?std.Thread,
1212
13/// Uses `Io.Clock.awake`.13/// Uses `Io.Clock.awake`.
14base_timestamp: i96,14base_timestamp: Io.Timestamp,
15/// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`.15/// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`.
16step_names_trailing: []u8,16step_names_trailing: []u8,
1717
...@@ -43,6 +43,8 @@ runner_request: ?RunnerRequest,...@@ -43,6 +43,8 @@ runner_request: ?RunnerRequest,
43/// on a fixed interval of this many milliseconds.43/// on a fixed interval of this many milliseconds.
44const default_update_interval_ms = 500;44const default_update_interval_ms = 500;
4545
46pub const base_clock: Io.Clock = .awake;
47
46/// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`.48/// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`.
47pub fn notifyUpdate(ws: *WebServer) void {49pub fn notifyUpdate(ws: *WebServer) void {
48 _ = ws.update_id.rmw(.Add, 1, .release);50 _ = ws.update_id.rmw(.Add, 1, .release);
...@@ -58,13 +60,13 @@ pub const Options = struct {...@@ -58,13 +60,13 @@ pub const Options = struct {
58 root_prog_node: std.Progress.Node,60 root_prog_node: std.Progress.Node,
59 watch: bool,61 watch: bool,
60 listen_address: net.IpAddress,62 listen_address: net.IpAddress,
61 base_timestamp: Io.Timestamp,63 base_timestamp: Io.Clock.Timestamp,
62};64};
63pub fn init(opts: Options) WebServer {65pub fn init(opts: Options) WebServer {
64 // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent`66 // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent`
65 // instead of threads, so that the web server can function in single-threaded builds.67 // instead of threads, so that the web server can function in single-threaded builds.
66 comptime assert(!builtin.single_threaded);68 comptime assert(!builtin.single_threaded);
67 assert(opts.base_timestamp.clock == .awake);69 assert(opts.base_timestamp.clock == base_clock);
6870
69 const all_steps = opts.all_steps;71 const all_steps = opts.all_steps;
7072
...@@ -109,7 +111,7 @@ pub fn init(opts: Options) WebServer {...@@ -109,7 +111,7 @@ pub fn init(opts: Options) WebServer {
109 .tcp_server = null,111 .tcp_server = null,
110 .serve_thread = null,112 .serve_thread = null,
111113
112 .base_timestamp = opts.base_timestamp.nanoseconds,114 .base_timestamp = opts.base_timestamp.raw,
113 .step_names_trailing = step_names_trailing,115 .step_names_trailing = step_names_trailing,
114116
115 .step_status_bits = step_status_bits,117 .step_status_bits = step_status_bits,
...@@ -248,9 +250,8 @@ pub fn finishBuild(ws: *WebServer, opts: struct {...@@ -248,9 +250,8 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
248250
249pub fn now(s: *const WebServer) i64 {251pub fn now(s: *const WebServer) i64 {
250 const io = s.graph.io;252 const io = s.graph.io;
251 const base: Io.Timestamp = .{ .nanoseconds = s.base_timestamp, .clock = .awake };253 const ts = base_clock.now(io) catch s.base_timestamp;
252 const ts = Io.Timestamp.now(io, base.clock) catch base;254 return @intCast(s.base_timestamp.durationTo(ts).toNanoseconds());
253 return @intCast(base.durationTo(ts).toNanoseconds());
254}255}
255256
256fn accept(ws: *WebServer, stream: net.Stream) void {257fn accept(ws: *WebServer, stream: net.Stream) void {
...@@ -519,7 +520,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons...@@ -519,7 +520,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons
519 if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa);520 if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa);
520 break :cwd cached_cwd_path.?;521 break :cwd cached_cwd_path.?;
521 };522 };
522 try archiver.writeFile(path.sub_path, &file_reader, stat.mtime);523 try archiver.writeFile(path.sub_path, &file_reader, @intCast(stat.mtime.toSeconds()));
523 }524 }
524525
525 // intentionally not calling `archiver.finishPedantically`526 // intentionally not calling `archiver.finishPedantically`
lib/std/Io.zig+155-104
...@@ -669,7 +669,7 @@ pub const VTable = struct {...@@ -669,7 +669,7 @@ pub const VTable = struct {
669 fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void,669 fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void,
670 fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void,670 fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void,
671671
672 now: *const fn (?*anyopaque, Timestamp.Clock) Timestamp.Error!i96,672 now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,
673 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,673 sleep: *const fn (?*anyopaque, Timeout) SleepError!void,
674674
675 listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,675 listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,
...@@ -705,118 +705,178 @@ pub const UnexpectedError = error{...@@ -705,118 +705,178 @@ pub const UnexpectedError = error{
705pub const Dir = @import("Io/Dir.zig");705pub const Dir = @import("Io/Dir.zig");
706pub const File = @import("Io/File.zig");706pub const File = @import("Io/File.zig");
707707
708pub const Timestamp = struct {708pub const Clock = enum {
709 nanoseconds: i96,709 /// A settable system-wide clock that measures real (i.e. wall-clock)
710 clock: Clock,710 /// time. This clock is affected by discontinuous jumps in the system
711711 /// time (e.g., if the system administrator manually changes the
712 pub const Clock = enum {712 /// clock), and by frequency adjust‐ ments performed by NTP and similar
713 /// A settable system-wide clock that measures real (i.e. wall-clock)713 /// applications.
714 /// time. This clock is affected by discontinuous jumps in the system714 ///
715 /// time (e.g., if the system administrator manually changes the715 /// This clock normally counts the number of seconds since 1970-01-01
716 /// clock), and by frequency adjust‐ ments performed by NTP and similar716 /// 00:00:00 Coordinated Universal Time (UTC) except that it ignores
717 /// applications.717 /// leap seconds; near a leap second it is typically adjusted by NTP to
718 ///718 /// stay roughly in sync with UTC.
719 /// This clock normally counts the number of seconds since 1970-01-01719 ///
720 /// 00:00:00 Coordinated Universal Time (UTC) except that it ignores720 /// The epoch is implementation-defined. For example NTFS/Windows uses
721 /// leap seconds; near a leap second it is typically adjusted by NTP to721 /// 1601-01-01.
722 /// stay roughly in sync with UTC.722 real,
723 ///723 /// A nonsettable system-wide clock that represents time since some
724 /// The epoch is implementation-defined. For example NTFS/Windows uses724 /// unspecified point in the past.
725 /// 1601-01-01.725 ///
726 real,726 /// Monotonic: Guarantees that the time returned by consecutive calls
727 /// A nonsettable system-wide clock that represents time since some727 /// will not go backwards, but successive calls may return identical
728 /// unspecified point in the past.728 /// (not-increased) time values.
729 ///729 ///
730 /// Monotonic: Guarantees that the time returned by consecutive calls730 /// Not affected by discontinuous jumps in the system time (e.g., if
731 /// will not go backwards, but successive calls may return identical731 /// the system administrator manually changes the clock), but may be
732 /// (not-increased) time values.732 /// affected by frequency adjustments.
733 ///733 ///
734 /// Not affected by discontinuous jumps in the system time (e.g., if734 /// This clock expresses intent to **exclude time that the system is
735 /// the system administrator manually changes the clock), but may be735 /// suspended**. However, implementations may be unable to satisify
736 /// affected by frequency adjustments.736 /// this, and may include that time.
737 ///737 ///
738 /// This clock expresses intent to **exclude time that the system is738 /// * On Linux, corresponds `CLOCK_MONOTONIC`.
739 /// suspended**. However, implementations may be unable to satisify739 /// * On macOS, corresponds to `CLOCK_UPTIME_RAW`.
740 /// this, and may include that time.740 awake,
741 ///741 /// Identical to `awake` except it expresses intent to **include time
742 /// * On Linux, corresponds `CLOCK_MONOTONIC`.742 /// that the system is suspended**, however, due to limitations it may
743 /// * On macOS, corresponds to `CLOCK_UPTIME_RAW`.743 /// behave identically to `awake`.
744 awake,744 ///
745 /// Identical to `awake` except it expresses intent to **include time745 /// * On Linux, corresponds `CLOCK_BOOTTIME`.
746 /// that the system is suspended**, however, due to limitations it may746 /// * On macOS, corresponds to `CLOCK_MONOTONIC_RAW`.
747 /// behave identically to `awake`.747 boot,
748 ///748 /// Tracks the amount of CPU in user or kernel mode used by the calling
749 /// * On Linux, corresponds `CLOCK_BOOTTIME`.749 /// process.
750 /// * On macOS, corresponds to `CLOCK_MONOTONIC_RAW`.750 cpu_process,
751 boot,751 /// Tracks the amount of CPU in user or kernel mode used by the calling
752 /// Tracks the amount of CPU in user or kernel mode used by the calling752 /// thread.
753 /// process.753 cpu_thread,
754 cpu_process,
755 /// Tracks the amount of CPU in user or kernel mode used by the calling
756 /// thread.
757 cpu_thread,
758 };
759
760 pub fn durationTo(from: Timestamp, to: Timestamp) Duration {
761 assert(from.clock == to.clock);
762 return .{ .nanoseconds = to.nanoseconds - from.nanoseconds };
763 }
764
765 pub fn addDuration(from: Timestamp, duration: Duration) Timestamp {
766 return .{
767 .nanoseconds = from.nanoseconds + duration.nanoseconds,
768 .clock = from.clock,
769 };
770 }
771754
772 pub const Error = error{UnsupportedClock} || UnexpectedError;755 pub const Error = error{UnsupportedClock} || UnexpectedError;
773756
774 /// This function is not cancelable because first of all it does not block,757 /// This function is not cancelable because first of all it does not block,
775 /// but more importantly, the cancelation logic itself may want to check758 /// but more importantly, the cancelation logic itself may want to check
776 /// the time.759 /// the time.
777 pub fn now(io: Io, clock: Clock) Error!Timestamp {760 pub fn now(clock: Clock, io: Io) Error!Io.Timestamp {
778 return .{761 return io.vtable.now(io.userdata, clock);
779 .nanoseconds = try io.vtable.now(io.userdata, clock),
780 .clock = clock,
781 };
782 }762 }
783763
784 pub fn fromNow(io: Io, clock: Clock, duration: Duration) Error!Timestamp {764 pub const Timestamp = struct {
785 const now_ts = try now(io, clock);765 raw: Io.Timestamp,
786 return addDuration(now_ts, duration);766 clock: Clock,
787 }767
768 /// This function is not cancelable because first of all it does not block,
769 /// but more importantly, the cancelation logic itself may want to check
770 /// the time.
771 pub fn now(io: Io, clock: Clock) Error!Clock.Timestamp {
772 return .{
773 .raw = try io.vtable.now(io.userdata, clock),
774 .clock = clock,
775 };
776 }
788777
789 pub fn untilNow(timestamp: Timestamp, io: Io) Error!Duration {778 pub fn wait(t: Clock.Timestamp, io: Io) SleepError!void {
790 const now_ts = try Timestamp.now(io, timestamp.clock);779 return io.vtable.sleep(io.userdata, .{ .deadline = t });
791 return timestamp.durationTo(now_ts);780 }
792 }781
782 pub fn durationTo(from: Clock.Timestamp, to: Clock.Timestamp) Clock.Duration {
783 assert(from.clock == to.clock);
784 return .{
785 .raw = from.raw.durationTo(to.raw),
786 .clock = from.clock,
787 };
788 }
793789
794 pub fn durationFromNow(timestamp: Timestamp, io: Io) Error!Duration {790 pub fn addDuration(from: Clock.Timestamp, duration: Clock.Duration) Clock.Timestamp {
795 const now_ts = try now(io, timestamp.clock);791 assert(from.clock == duration.clock);
796 return now_ts.durationTo(timestamp);792 return .{
793 .raw = from.raw.addDuration(duration.raw),
794 .clock = from.clock,
795 };
796 }
797
798 pub fn fromNow(io: Io, duration: Clock.Duration) Error!Clock.Timestamp {
799 return .{
800 .clock = duration.clock,
801 .raw = (try duration.clock.now(io)).addDuration(duration.raw),
802 };
803 }
804
805 pub fn untilNow(timestamp: Clock.Timestamp, io: Io) Error!Clock.Duration {
806 const now_ts = try Clock.Timestamp.now(io, timestamp.clock);
807 return timestamp.durationTo(now_ts);
808 }
809
810 pub fn durationFromNow(timestamp: Clock.Timestamp, io: Io) Error!Clock.Duration {
811 const now_ts = try timestamp.clock.now(io);
812 return .{
813 .clock = timestamp.clock,
814 .raw = now_ts.durationTo(timestamp.raw),
815 };
816 }
817
818 pub fn toClock(t: Clock.Timestamp, io: Io, clock: Clock) Error!Clock.Timestamp {
819 if (t.clock == clock) return t;
820 const now_old = try t.clock.now(io);
821 const now_new = try clock.now(io);
822 const duration = now_old.durationTo(t);
823 return .{
824 .clock = clock,
825 .raw = now_new.addDuration(duration),
826 };
827 }
828
829 pub fn compare(lhs: Clock.Timestamp, op: std.math.CompareOperator, rhs: Clock.Timestamp) bool {
830 assert(lhs.clock == rhs.clock);
831 return std.math.compare(lhs.raw.nanoseconds, op, rhs.raw.nanoseconds);
832 }
833 };
834
835 pub const Duration = struct {
836 raw: Io.Duration,
837 clock: Clock,
838
839 pub fn sleep(duration: Clock.Duration, io: Io) SleepError!void {
840 return io.vtable.sleep(io.userdata, .{ .duration = duration });
841 }
842 };
843};
844
845pub const Timestamp = struct {
846 nanoseconds: i96,
847
848 pub const zero: Timestamp = .{ .nanoseconds = 0 };
849
850 pub fn durationTo(from: Timestamp, to: Timestamp) Duration {
851 return .{ .nanoseconds = to.nanoseconds - from.nanoseconds };
797 }852 }
798853
799 pub fn toClock(t: Timestamp, io: Io, clock: Clock) Error!Timestamp {854 pub fn addDuration(from: Timestamp, duration: Duration) Timestamp {
800 if (t.clock == clock) return t;855 return .{ .nanoseconds = from.nanoseconds + duration.nanoseconds };
801 const now_old = try now(io, t.clock);
802 const now_new = try now(io, clock);
803 const duration = now_old.durationTo(t);
804 return now_new.addDuration(duration);
805 }856 }
806857
807 pub fn compare(lhs: Timestamp, op: std.math.CompareOperator, rhs: Timestamp) bool {858 pub fn withClock(t: Timestamp, clock: Clock) Clock.Timestamp {
808 assert(lhs.clock == rhs.clock);859 return .{ .nanoseconds = t.nanoseconds, .clock = clock };
809 return std.math.compare(lhs.nanoseconds, op, rhs.nanoseconds);
810 }860 }
811861
812 pub fn toSeconds(t: Timestamp) i64 {862 pub fn toSeconds(t: Timestamp) i64 {
813 return @intCast(@divTrunc(t.nanoseconds, std.time.ns_per_s));863 return @intCast(@divTrunc(t.nanoseconds, std.time.ns_per_s));
814 }864 }
865
866 pub fn formatNumber(t: Timestamp, w: *std.Io.Writer, n: std.fmt.Number) std.Io.Writer.Error!void {
867 return w.printInt(t.nanoseconds, n.mode.base() orelse 10, n.case, .{
868 .precision = n.precision,
869 .width = n.width,
870 .alignment = n.alignment,
871 .fill = n.fill,
872 });
873 }
815};874};
816875
817pub const Duration = struct {876pub const Duration = struct {
818 nanoseconds: i96,877 nanoseconds: i96,
819878
879 pub const zero: Duration = .{ .nanoseconds = 0 };
820 pub const max: Duration = .{ .nanoseconds = std.math.maxInt(i96) };880 pub const max: Duration = .{ .nanoseconds = std.math.maxInt(i96) };
821881
822 pub fn fromNanoseconds(x: i96) Duration {882 pub fn fromNanoseconds(x: i96) Duration {
...@@ -842,38 +902,29 @@ pub const Duration = struct {...@@ -842,38 +902,29 @@ pub const Duration = struct {
842 pub fn toNanoseconds(d: Duration) i96 {902 pub fn toNanoseconds(d: Duration) i96 {
843 return d.nanoseconds;903 return d.nanoseconds;
844 }904 }
845
846 pub fn sleep(duration: Duration, io: Io) SleepError!void {
847 return io.vtable.sleep(io.userdata, .{ .duration = .{ .duration = duration, .clock = .awake } });
848 }
849};905};
850906
851/// Declares under what conditions an operation should return `error.Timeout`.907/// Declares under what conditions an operation should return `error.Timeout`.
852pub const Timeout = union(enum) {908pub const Timeout = union(enum) {
853 none,909 none,
854 duration: ClockAndDuration,910 duration: Clock.Duration,
855 deadline: Timestamp,911 deadline: Clock.Timestamp,
856912
857 pub const Error = error{ Timeout, UnsupportedClock };913 pub const Error = error{ Timeout, UnsupportedClock };
858914
859 pub const ClockAndDuration = struct {915 pub fn toDeadline(t: Timeout, io: Io) Clock.Error!?Clock.Timestamp {
860 clock: Timestamp.Clock,
861 duration: Duration,
862 };
863
864 pub fn toDeadline(t: Timeout, io: Io) Timestamp.Error!?Timestamp {
865 return switch (t) {916 return switch (t) {
866 .none => null,917 .none => null,
867 .duration => |d| try .fromNow(io, d.clock, d.duration),918 .duration => |d| try .fromNow(io, d),
868 .deadline => |d| d,919 .deadline => |d| d,
869 };920 };
870 }921 }
871922
872 pub fn toDurationFromNow(t: Timeout, io: Io) Timestamp.Error!?ClockAndDuration {923 pub fn toDurationFromNow(t: Timeout, io: Io) Clock.Error!?Clock.Duration {
873 return switch (t) {924 return switch (t) {
874 .none => null,925 .none => null,
875 .duration => |d| d,926 .duration => |d| d,
876 .deadline => |d| .{ .clock = d.clock, .duration = try d.durationFromNow(io) },927 .deadline => |d| try d.durationFromNow(io),
877 };928 };
878 }929 }
879930
lib/std/Io/Dir.zig+1-1
...@@ -85,7 +85,7 @@ pub fn updateFile(...@@ -85,7 +85,7 @@ pub fn updateFile(
85 };85 };
8686
87 if (src_stat.size == dest_stat.size and87 if (src_stat.size == dest_stat.size and
88 src_stat.mtime == dest_stat.mtime and88 src_stat.mtime.nanoseconds == dest_stat.mtime.nanoseconds and
89 actual_mode == dest_stat.mode)89 actual_mode == dest_stat.mode)
90 {90 {
91 return .fresh;91 return .fresh;
lib/std/Io/File.zig+3-7
...@@ -45,16 +45,12 @@ pub const Stat = struct {...@@ -45,16 +45,12 @@ pub const Stat = struct {
45 /// This is available on POSIX systems and is always 0 otherwise.45 /// This is available on POSIX systems and is always 0 otherwise.
46 mode: Mode,46 mode: Mode,
47 kind: Kind,47 kind: Kind,
48
49 /// Last access time in nanoseconds, relative to UTC 1970-01-01.48 /// Last access time in nanoseconds, relative to UTC 1970-01-01.
50 /// TODO change this to Io.Timestamp except don't waste storage on clock49 atime: Io.Timestamp,
51 atime: i128,
52 /// Last modification time in nanoseconds, relative to UTC 1970-01-01.50 /// Last modification time in nanoseconds, relative to UTC 1970-01-01.
53 /// TODO change this to Io.Timestamp except don't waste storage on clock51 mtime: Io.Timestamp,
54 mtime: i128,
55 /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.52 /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.
56 /// TODO change this to Io.Timestamp except don't waste storage on clock53 ctime: Io.Timestamp,
57 ctime: i128,
58};54};
5955
60pub fn stdout() File {56pub fn stdout() File {
lib/std/Io/Threaded.zig+30-26
...@@ -1147,26 +1147,26 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi...@@ -1147,26 +1147,26 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi
1147 };1147 };
1148}1148}
11491149
1150fn nowPosix(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 {1150fn nowPosix(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
1151 const pool: *Pool = @ptrCast(@alignCast(userdata));1151 const pool: *Pool = @ptrCast(@alignCast(userdata));
1152 _ = pool;1152 _ = pool;
1153 const clock_id: posix.clockid_t = clockToPosix(clock);1153 const clock_id: posix.clockid_t = clockToPosix(clock);
1154 var tp: posix.timespec = undefined;1154 var tp: posix.timespec = undefined;
1155 switch (posix.errno(posix.system.clock_gettime(clock_id, &tp))) {1155 switch (posix.errno(posix.system.clock_gettime(clock_id, &tp))) {
1156 .SUCCESS => return @intCast(@as(i128, tp.sec) * std.time.ns_per_s + tp.nsec),1156 .SUCCESS => return timestampFromPosix(&tp),
1157 .INVAL => return error.UnsupportedClock,1157 .INVAL => return error.UnsupportedClock,
1158 else => |err| return posix.unexpectedErrno(err),1158 else => |err| return posix.unexpectedErrno(err),
1159 }1159 }
1160}1160}
11611161
1162fn nowWindows(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 {1162fn nowWindows(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
1163 const pool: *Pool = @ptrCast(@alignCast(userdata));1163 const pool: *Pool = @ptrCast(@alignCast(userdata));
1164 _ = pool;1164 _ = pool;
1165 switch (clock) {1165 switch (clock) {
1166 .realtime => {1166 .realtime => {
1167 // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds1167 // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds
1168 // and uses the NTFS/Windows epoch, which is 1601-01-01.1168 // and uses the NTFS/Windows epoch, which is 1601-01-01.
1169 return @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100;1169 return .{ .nanoseconds = @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100 };
1170 },1170 },
1171 .monotonic, .uptime => {1171 .monotonic, .uptime => {
1172 // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.1172 // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
...@@ -1178,7 +1178,7 @@ fn nowWindows(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Err...@@ -1178,7 +1178,7 @@ fn nowWindows(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Err
1178 }1178 }
1179}1179}
11801180
1181fn nowWasi(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 {1181fn nowWasi(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
1182 const pool: *Pool = @ptrCast(@alignCast(userdata));1182 const pool: *Pool = @ptrCast(@alignCast(userdata));
1183 _ = pool;1183 _ = pool;
1184 var ns: std.os.wasi.timestamp_t = undefined;1184 var ns: std.os.wasi.timestamp_t = undefined;
...@@ -1196,13 +1196,10 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -1196,13 +1196,10 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1196 });1196 });
1197 const deadline_nanoseconds: i96 = switch (timeout) {1197 const deadline_nanoseconds: i96 = switch (timeout) {
1198 .none => std.math.maxInt(i96),1198 .none => std.math.maxInt(i96),
1199 .duration => |d| d.duration.nanoseconds,1199 .duration => |duration| duration.raw.nanoseconds,
1200 .deadline => |deadline| deadline.nanoseconds,1200 .deadline => |deadline| deadline.raw.nanoseconds,
1201 };
1202 var timespec: posix.timespec = .{
1203 .sec = @intCast(@divFloor(deadline_nanoseconds, std.time.ns_per_s)),
1204 .nsec = @intCast(@mod(deadline_nanoseconds, std.time.ns_per_s)),
1205 };1201 };
1202 var timespec: posix.timespec = timestampToPosix(deadline_nanoseconds);
1206 while (true) {1203 while (true) {
1207 try pool.checkCancel();1204 try pool.checkCancel();
1208 switch (std.os.linux.E.init(std.os.linux.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) {1205 switch (std.os.linux.E.init(std.os.linux.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) {
...@@ -1267,11 +1264,7 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -1267,11 +1264,7 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1267 .sec = std.math.maxInt(sec_type),1264 .sec = std.math.maxInt(sec_type),
1268 .nsec = std.math.maxInt(nsec_type),1265 .nsec = std.math.maxInt(nsec_type),
1269 };1266 };
1270 const ns = d.duration.nanoseconds;1267 break :t timestampToPosix(d.duration.nanoseconds);
1271 break :t .{
1272 .sec = @intCast(@divFloor(ns, std.time.ns_per_s)),
1273 .nsec = @intCast(@mod(ns, std.time.ns_per_s)),
1274 };
1275 };1268 };
1276 while (true) {1269 while (true) {
1277 try pool.checkCancel();1270 try pool.checkCancel();
...@@ -1879,8 +1872,8 @@ fn netReceive(...@@ -1879,8 +1872,8 @@ fn netReceive(
1879 const max_poll_ms = std.math.maxInt(u31);1872 const max_poll_ms = std.math.maxInt(u31);
1880 const timeout_ms: u31 = if (deadline) |d| t: {1873 const timeout_ms: u31 = if (deadline) |d| t: {
1881 const duration = d.durationFromNow(pool.io()) catch |err| return .{ err, message_i };1874 const duration = d.durationFromNow(pool.io()) catch |err| return .{ err, message_i };
1882 if (duration.nanoseconds <= 0) return .{ error.Timeout, message_i };1875 if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i };
1883 break :t @intCast(@min(max_poll_ms, duration.toMilliseconds()));1876 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));
1884 } else max_poll_ms;1877 } else max_poll_ms;
18851878
1886 const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms);1879 const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms);
...@@ -2160,7 +2153,7 @@ fn recoverableOsBugDetected() void {...@@ -2160,7 +2153,7 @@ fn recoverableOsBugDetected() void {
2160 if (builtin.mode == .Debug) unreachable;2153 if (builtin.mode == .Debug) unreachable;
2161}2154}
21622155
2163fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {2156fn clockToPosix(clock: Io.Clock) posix.clockid_t {
2164 return switch (clock) {2157 return switch (clock) {
2165 .real => posix.CLOCK.REALTIME,2158 .real => posix.CLOCK.REALTIME,
2166 .awake => switch (builtin.os.tag) {2159 .awake => switch (builtin.os.tag) {
...@@ -2176,7 +2169,7 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {...@@ -2176,7 +2169,7 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {
2176 };2169 };
2177}2170}
21782171
2179fn clockToWasi(clock: Io.Timestamp.Clock) std.os.wasi.clockid_t {2172fn clockToWasi(clock: Io.Clock) std.os.wasi.clockid_t {
2180 return switch (clock) {2173 return switch (clock) {
2181 .realtime => .REALTIME,2174 .realtime => .REALTIME,
2182 .awake => .MONOTONIC,2175 .awake => .MONOTONIC,
...@@ -2204,9 +2197,9 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.File.Stat {...@@ -2204,9 +2197,9 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.File.Stat {
2204 std.os.linux.S.IFSOCK => .unix_domain_socket,2197 std.os.linux.S.IFSOCK => .unix_domain_socket,
2205 else => .unknown,2198 else => .unknown,
2206 },2199 },
2207 .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec,2200 .atime = .{ .nanoseconds = @intCast(@as(i128, atime.sec) * std.time.ns_per_s + atime.nsec) },
2208 .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec,2201 .mtime = .{ .nanoseconds = @intCast(@as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec) },
2209 .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec,2202 .ctime = .{ .nanoseconds = @intCast(@as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec) },
2210 };2203 };
2211}2204}
22122205
...@@ -2238,9 +2231,9 @@ fn statFromPosix(st: *const std.posix.Stat) Io.File.Stat {...@@ -2238,9 +2231,9 @@ fn statFromPosix(st: *const std.posix.Stat) Io.File.Stat {
22382231
2239 break :k .unknown;2232 break :k .unknown;
2240 },2233 },
2241 .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec,2234 .atime = timestampFromPosix(&atime),
2242 .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec,2235 .mtime = timestampFromPosix(&mtime),
2243 .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec,2236 .ctime = timestampFromPosix(&ctime),
2244 };2237 };
2245}2238}
22462239
...@@ -2263,3 +2256,14 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat {...@@ -2263,3 +2256,14 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat {
2263 .ctime = st.ctim,2256 .ctime = st.ctim,
2264 };2257 };
2265}2258}
2259
2260fn timestampFromPosix(timespec: *const std.posix.timespec) Io.Timestamp {
2261 return .{ .nanoseconds = @intCast(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec) };
2262}
2263
2264fn timestampToPosix(nanoseconds: i96) std.posix.timespec {
2265 return .{
2266 .sec = @intCast(@divFloor(nanoseconds, std.time.ns_per_s)),
2267 .nsec = @intCast(@mod(nanoseconds, std.time.ns_per_s)),
2268 };
2269}
lib/std/Io/net/HostName.zig+8-4
...@@ -79,7 +79,7 @@ pub const LookupError = error{...@@ -79,7 +79,7 @@ pub const LookupError = error{
79 NameServerFailure,79 NameServerFailure,
80 /// Failed to open or read "/etc/hosts" or "/etc/resolv.conf".80 /// Failed to open or read "/etc/hosts" or "/etc/resolv.conf".
81 DetectingNetworkConfigurationFailed,81 DetectingNetworkConfigurationFailed,
82} || Io.Timestamp.Error || IpAddress.BindError || Io.Cancelable;82} || Io.Clock.Error || IpAddress.BindError || Io.Cancelable;
8383
84pub const LookupResult = struct {84pub const LookupResult = struct {
85 /// How many `LookupOptions.addresses_buffer` elements are populated.85 /// How many `LookupOptions.addresses_buffer` elements are populated.
...@@ -294,13 +294,14 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio...@@ -294,13 +294,14 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio
294294
295 // boot clock is chosen because time the computer is suspended should count295 // boot clock is chosen because time the computer is suspended should count
296 // against time spent waiting for external messages to arrive.296 // against time spent waiting for external messages to arrive.
297 var now_ts = try Io.Timestamp.now(io, .boot);297 const clock: Io.Clock = .boot;
298 var now_ts = try clock.now(io);
298 const final_ts = now_ts.addDuration(.fromSeconds(rc.timeout_seconds));299 const final_ts = now_ts.addDuration(.fromSeconds(rc.timeout_seconds));
299 const attempt_duration: Io.Duration = .{300 const attempt_duration: Io.Duration = .{
300 .nanoseconds = std.time.ns_per_s * @as(usize, rc.timeout_seconds) / rc.attempts,301 .nanoseconds = std.time.ns_per_s * @as(usize, rc.timeout_seconds) / rc.attempts,
301 };302 };
302303
303 send: while (now_ts.compare(.lt, final_ts)) : (now_ts = try Io.Timestamp.now(io, .boot)) {304 send: while (now_ts.nanoseconds < final_ts.nanoseconds) : (now_ts = try clock.now(io)) {
304 const max_messages = queries_buffer.len * ResolvConf.max_nameservers;305 const max_messages = queries_buffer.len * ResolvConf.max_nameservers;
305 {306 {
306 var message_buffer: [max_messages]Io.net.OutgoingMessage = undefined;307 var message_buffer: [max_messages]Io.net.OutgoingMessage = undefined;
...@@ -319,7 +320,10 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio...@@ -319,7 +320,10 @@ fn lookupDns(io: Io, lookup_canon_name: []const u8, rc: *const ResolvConf, optio
319 _ = io.vtable.netSend(io.userdata, socket.handle, message_buffer[0..message_i], .{});320 _ = io.vtable.netSend(io.userdata, socket.handle, message_buffer[0..message_i], .{});
320 }321 }
321322
322 const timeout: Io.Timeout = .{ .deadline = now_ts.addDuration(attempt_duration) };323 const timeout: Io.Timeout = .{ .deadline = .{
324 .raw = now_ts.addDuration(attempt_duration),
325 .clock = clock,
326 } };
323327
324 while (true) {328 while (true) {
325 var message_buffer: [max_messages]Io.net.IncomingMessage = undefined;329 var message_buffer: [max_messages]Io.net.IncomingMessage = undefined;
lib/std/fs/File.zig+8-8
...@@ -637,23 +637,23 @@ pub const UpdateTimesError = posix.FutimensError || windows.SetFileTimeError;...@@ -637,23 +637,23 @@ pub const UpdateTimesError = posix.FutimensError || windows.SetFileTimeError;
637pub fn updateTimes(637pub fn updateTimes(
638 self: File,638 self: File,
639 /// access timestamp in nanoseconds639 /// access timestamp in nanoseconds
640 atime: i128,640 atime: Io.Timestamp,
641 /// last modification timestamp in nanoseconds641 /// last modification timestamp in nanoseconds
642 mtime: i128,642 mtime: Io.Timestamp,
643) UpdateTimesError!void {643) UpdateTimesError!void {
644 if (builtin.os.tag == .windows) {644 if (builtin.os.tag == .windows) {
645 const atime_ft = windows.nanoSecondsToFileTime(atime);645 const atime_ft = windows.nanoSecondsToFileTime(atime.nanoseconds);
646 const mtime_ft = windows.nanoSecondsToFileTime(mtime);646 const mtime_ft = windows.nanoSecondsToFileTime(mtime.nanoseconds);
647 return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft);647 return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft);
648 }648 }
649 const times = [2]posix.timespec{649 const times = [2]posix.timespec{
650 posix.timespec{650 posix.timespec{
651 .sec = math.cast(isize, @divFloor(atime, std.time.ns_per_s)) orelse maxInt(isize),651 .sec = math.cast(isize, @divFloor(atime.nanoseconds, std.time.ns_per_s)) orelse maxInt(isize),
652 .nsec = math.cast(isize, @mod(atime, std.time.ns_per_s)) orelse maxInt(isize),652 .nsec = math.cast(isize, @mod(atime.nanoseconds, std.time.ns_per_s)) orelse maxInt(isize),
653 },653 },
654 posix.timespec{654 posix.timespec{
655 .sec = math.cast(isize, @divFloor(mtime, std.time.ns_per_s)) orelse maxInt(isize),655 .sec = math.cast(isize, @divFloor(mtime.nanoseconds, std.time.ns_per_s)) orelse maxInt(isize),
656 .nsec = math.cast(isize, @mod(mtime, std.time.ns_per_s)) orelse maxInt(isize),656 .nsec = math.cast(isize, @mod(mtime.nanoseconds, std.time.ns_per_s)) orelse maxInt(isize),
657 },657 },
658 };658 };
659 try posix.futimens(self.handle, &times);659 try posix.futimens(self.handle, &times);
lib/std/http/Client.zig+1-1
...@@ -320,7 +320,7 @@ pub const Connection = struct {...@@ -320,7 +320,7 @@ pub const Connection = struct {
320 const tls: *Tls = @ptrCast(base);320 const tls: *Tls = @ptrCast(base);
321 var random_buffer: [176]u8 = undefined;321 var random_buffer: [176]u8 = undefined;
322 std.crypto.random.bytes(&random_buffer);322 std.crypto.random.bytes(&random_buffer);
323 const now_ts = if (Io.Timestamp.now(io, .real)) |ts| ts.toSeconds() else |_| return error.TlsInitializationFailed;323 const now_ts = if (Io.Clock.real.now(io)) |ts| ts.toSeconds() else |_| return error.TlsInitializationFailed;
324 tls.* = .{324 tls.* = .{
325 .connection = .{325 .connection = .{
326 .client = client,326 .client = client,
lib/std/tar/Writer.zig+4-4
...@@ -18,7 +18,6 @@ pub const Options = struct {...@@ -18,7 +18,6 @@ pub const Options = struct {
1818
19underlying_writer: *Io.Writer,19underlying_writer: *Io.Writer,
20prefix: []const u8 = "",20prefix: []const u8 = "",
21mtime_now: u64 = 0,
2221
23const Error = error{22const Error = error{
24 WriteFailed,23 WriteFailed,
...@@ -44,10 +43,12 @@ pub fn writeFile(...@@ -44,10 +43,12 @@ pub fn writeFile(
44 w: *Writer,43 w: *Writer,
45 sub_path: []const u8,44 sub_path: []const u8,
46 file_reader: *Io.File.Reader,45 file_reader: *Io.File.Reader,
47 stat_mtime: i128,46 /// If you want to match the file format's expectations, it wants number of
47 /// seconds since POSIX epoch. Zero is also a great option here to make
48 /// generated tarballs more reproducible.
49 mtime: u64,
48) WriteFileError!void {50) WriteFileError!void {
49 const size = try file_reader.getSize();51 const size = try file_reader.getSize();
50 const mtime: u64 = @intCast(@divFloor(stat_mtime, std.time.ns_per_s));
5152
52 var header: Header = .{};53 var header: Header = .{};
53 try w.setPath(&header, sub_path);54 try w.setPath(&header, sub_path);
...@@ -238,7 +239,6 @@ pub const Header = extern struct {...@@ -238,7 +239,6 @@ pub const Header = extern struct {
238 }239 }
239240
240 // Integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time.241 // Integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time.
241 // mtime == 0 will use current time
242 pub fn setMtime(w: *Header, mtime: u64) error{OctalOverflow}!void {242 pub fn setMtime(w: *Header, mtime: u64) error{OctalOverflow}!void {
243 try octal(&w.mtime, mtime);243 try octal(&w.mtime, mtime);
244 }244 }