| author | |
| committer | |
| log | ebcc6f166c9c34d00b750f26687c9ee36b243cb0 |
| tree | 73ba685c9985284f2ebf5aee3b81fb30eaca9c86 |
| parent | 89412fda775aecdedf4047355f2c45b48334a285 |
this feels better11 files changed, 233 insertions(+), 177 deletions(-)
lib/compiler/build_runner.zig+1-1| ... | ... | @@ -556,7 +556,7 @@ pub fn main() !void { |
| 556 | 556 | try run.thread_pool.init(thread_pool_options); |
| 557 | 557 | defer run.thread_pool.deinit(); |
| 558 | 558 | |
| 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}); | |
| 560 | 560 | |
| 561 | 561 | run.web_server = if (webui_listen) |listen_address| ws: { |
| 562 | 562 | if (builtin.single_threaded) unreachable; // `fatal` above |
lib/std/Build/Cache.zig+13-13| ... | ... | @@ -21,7 +21,7 @@ io: Io, |
| 21 | 21 | manifest_dir: fs.Dir, |
| 22 | 22 | hash: HashHelper = .{}, |
| 23 | 23 | /// This value is accessed from multiple threads, protected by mutex. |
| 24 | recent_problematic_timestamp: i128 = 0, | |
| 24 | recent_problematic_timestamp: Io.Timestamp = .zero, | |
| 25 | 25 | mutex: std.Thread.Mutex = .{}, |
| 26 | 26 | |
| 27 | 27 | /// A set of strings such as the zig library directory or project source root, which |
| ... | ... | @@ -155,7 +155,7 @@ pub const File = struct { |
| 155 | 155 | pub const Stat = struct { |
| 156 | 156 | inode: fs.File.INode, |
| 157 | 157 | size: u64, |
| 158 | mtime: i128, | |
| 158 | mtime: Io.Timestamp, | |
| 159 | 159 | |
| 160 | 160 | pub fn fromFs(fs_stat: fs.File.Stat) Stat { |
| 161 | 161 | return .{ |
| ... | ... | @@ -330,7 +330,7 @@ pub const Manifest = struct { |
| 330 | 330 | diagnostic: Diagnostic = .none, |
| 331 | 331 | /// Keeps track of the last time we performed a file system write to observe |
| 332 | 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, | |
| 334 | 334 | |
| 335 | 335 | pub const Diagnostic = union(enum) { |
| 336 | 336 | none, |
| ... | ... | @@ -728,7 +728,7 @@ pub const Manifest = struct { |
| 728 | 728 | file.stat = .{ |
| 729 | 729 | .size = stat_size, |
| 730 | 730 | .inode = stat_inode, |
| 731 | .mtime = stat_mtime, | |
| 731 | .mtime = .{ .nanoseconds = stat_mtime }, | |
| 732 | 732 | }; |
| 733 | 733 | file.bin_digest = file_bin_digest; |
| 734 | 734 | break :f file; |
| ... | ... | @@ -747,7 +747,7 @@ pub const Manifest = struct { |
| 747 | 747 | .stat = .{ |
| 748 | 748 | .size = stat_size, |
| 749 | 749 | .inode = stat_inode, |
| 750 | .mtime = stat_mtime, | |
| 750 | .mtime = .{ .nanoseconds = stat_mtime }, | |
| 751 | 751 | }, |
| 752 | 752 | .bin_digest = file_bin_digest, |
| 753 | 753 | }; |
| ... | ... | @@ -780,7 +780,7 @@ pub const Manifest = struct { |
| 780 | 780 | return error.CacheCheckFailed; |
| 781 | 781 | }; |
| 782 | 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 | 784 | const inode_match = actual_stat.inode == cache_hash_file.stat.inode; |
| 785 | 785 | |
| 786 | 786 | if (!size_match or !mtime_match or !inode_match) { |
| ... | ... | @@ -792,7 +792,7 @@ pub const Manifest = struct { |
| 792 | 792 | |
| 793 | 793 | if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) { |
| 794 | 794 | // 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 | 796 | cache_hash_file.stat.inode = 0; |
| 797 | 797 | } |
| 798 | 798 | |
| ... | ... | @@ -848,10 +848,10 @@ pub const Manifest = struct { |
| 848 | 848 | } |
| 849 | 849 | } |
| 850 | 850 | |
| 851 | fn isProblematicTimestamp(man: *Manifest, file_time: i128) bool { | |
| 851 | fn isProblematicTimestamp(man: *Manifest, timestamp: Io.Timestamp) bool { | |
| 852 | 852 | // If the file_time is prior to the most recent problematic timestamp |
| 853 | 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 | 855 | return false; |
| 856 | 856 | |
| 857 | 857 | // Next we will check the globally shared Cache timestamp, which is accessed |
| ... | ... | @@ -861,7 +861,7 @@ pub const Manifest = struct { |
| 861 | 861 | |
| 862 | 862 | // Save the global one to our local one to avoid locking next time. |
| 863 | 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 | 865 | return false; |
| 866 | 866 | |
| 867 | 867 | // This flag prevents multiple filesystem writes for the same hit() call. |
| ... | ... | @@ -879,7 +879,7 @@ pub const Manifest = struct { |
| 879 | 879 | man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp; |
| 880 | 880 | } |
| 881 | 881 | |
| 882 | return file_time >= man.recent_problematic_timestamp; | |
| 882 | return timestamp.nanoseconds >= man.recent_problematic_timestamp.nanoseconds; | |
| 883 | 883 | } |
| 884 | 884 | |
| 885 | 885 | fn populateFileHash(self: *Manifest, ch_file: *File) !void { |
| ... | ... | @@ -904,7 +904,7 @@ pub const Manifest = struct { |
| 904 | 904 | |
| 905 | 905 | if (self.isProblematicTimestamp(ch_file.stat.mtime)) { |
| 906 | 906 | // 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 | 908 | ch_file.stat.inode = 0; |
| 909 | 909 | } |
| 910 | 910 | |
| ... | ... | @@ -1040,7 +1040,7 @@ pub const Manifest = struct { |
| 1040 | 1040 | |
| 1041 | 1041 | if (self.isProblematicTimestamp(new_file.stat.mtime)) { |
| 1042 | 1042 | // 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 | 1044 | new_file.stat.inode = 0; |
| 1045 | 1045 | } |
| 1046 | 1046 |
lib/std/Build/WebServer.zig+9-8| ... | ... | @@ -11,7 +11,7 @@ tcp_server: ?net.Server, |
| 11 | 11 | serve_thread: ?std.Thread, |
| 12 | 12 | |
| 13 | 13 | /// Uses `Io.Clock.awake`. |
| 14 | base_timestamp: i96, | |
| 14 | base_timestamp: Io.Timestamp, | |
| 15 | 15 | /// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`. |
| 16 | 16 | step_names_trailing: []u8, |
| 17 | 17 | |
| ... | ... | @@ -43,6 +43,8 @@ runner_request: ?RunnerRequest, |
| 43 | 43 | /// on a fixed interval of this many milliseconds. |
| 44 | 44 | const default_update_interval_ms = 500; |
| 45 | 45 | |
| 46 | pub const base_clock: Io.Clock = .awake; | |
| 47 | ||
| 46 | 48 | /// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`. |
| 47 | 49 | pub fn notifyUpdate(ws: *WebServer) void { |
| 48 | 50 | _ = ws.update_id.rmw(.Add, 1, .release); |
| ... | ... | @@ -58,13 +60,13 @@ pub const Options = struct { |
| 58 | 60 | root_prog_node: std.Progress.Node, |
| 59 | 61 | watch: bool, |
| 60 | 62 | listen_address: net.IpAddress, |
| 61 | base_timestamp: Io.Timestamp, | |
| 63 | base_timestamp: Io.Clock.Timestamp, | |
| 62 | 64 | }; |
| 63 | 65 | pub fn init(opts: Options) WebServer { |
| 64 | 66 | // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent` |
| 65 | 67 | // instead of threads, so that the web server can function in single-threaded builds. |
| 66 | 68 | comptime assert(!builtin.single_threaded); |
| 67 | assert(opts.base_timestamp.clock == .awake); | |
| 69 | assert(opts.base_timestamp.clock == base_clock); | |
| 68 | 70 | |
| 69 | 71 | const all_steps = opts.all_steps; |
| 70 | 72 | |
| ... | ... | @@ -109,7 +111,7 @@ pub fn init(opts: Options) WebServer { |
| 109 | 111 | .tcp_server = null, |
| 110 | 112 | .serve_thread = null, |
| 111 | 113 | |
| 112 | .base_timestamp = opts.base_timestamp.nanoseconds, | |
| 114 | .base_timestamp = opts.base_timestamp.raw, | |
| 113 | 115 | .step_names_trailing = step_names_trailing, |
| 114 | 116 | |
| 115 | 117 | .step_status_bits = step_status_bits, |
| ... | ... | @@ -248,9 +250,8 @@ pub fn finishBuild(ws: *WebServer, opts: struct { |
| 248 | 250 | |
| 249 | 251 | pub fn now(s: *const WebServer) i64 { |
| 250 | 252 | const io = s.graph.io; |
| 251 | const base: Io.Timestamp = .{ .nanoseconds = s.base_timestamp, .clock = .awake }; | |
| 252 | const ts = Io.Timestamp.now(io, base.clock) catch base; | |
| 253 | return @intCast(base.durationTo(ts).toNanoseconds()); | |
| 253 | const ts = base_clock.now(io) catch s.base_timestamp; | |
| 254 | return @intCast(s.base_timestamp.durationTo(ts).toNanoseconds()); | |
| 254 | 255 | } |
| 255 | 256 | |
| 256 | 257 | fn accept(ws: *WebServer, stream: net.Stream) void { |
| ... | ... | @@ -519,7 +520,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons |
| 519 | 520 | if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa); |
| 520 | 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 | } |
| 524 | 525 | |
| 525 | 526 | // intentionally not calling `archiver.finishPedantically` |
lib/std/Io.zig+155-104| ... | ... | @@ -669,7 +669,7 @@ pub const VTable = struct { |
| 669 | 669 | fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void, |
| 670 | 670 | fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void, |
| 671 | 671 | |
| 672 | now: *const fn (?*anyopaque, Timestamp.Clock) Timestamp.Error!i96, | |
| 672 | now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp, | |
| 673 | 673 | sleep: *const fn (?*anyopaque, Timeout) SleepError!void, |
| 674 | 674 | |
| 675 | 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 | 705 | pub const Dir = @import("Io/Dir.zig"); |
| 706 | 706 | pub const File = @import("Io/File.zig"); |
| 707 | 707 | |
| 708 | pub const Timestamp = struct { | |
| 709 | nanoseconds: i96, | |
| 710 | clock: Clock, | |
| 711 | ||
| 712 | pub const Clock = enum { | |
| 713 | /// A settable system-wide clock that measures real (i.e. wall-clock) | |
| 714 | /// time. This clock is affected by discontinuous jumps in the system | |
| 715 | /// time (e.g., if the system administrator manually changes the | |
| 716 | /// clock), and by frequency adjust‐ ments performed by NTP and similar | |
| 717 | /// applications. | |
| 718 | /// | |
| 719 | /// This clock normally counts the number of seconds since 1970-01-01 | |
| 720 | /// 00:00:00 Coordinated Universal Time (UTC) except that it ignores | |
| 721 | /// leap seconds; near a leap second it is typically adjusted by NTP to | |
| 722 | /// stay roughly in sync with UTC. | |
| 723 | /// | |
| 724 | /// The epoch is implementation-defined. For example NTFS/Windows uses | |
| 725 | /// 1601-01-01. | |
| 726 | real, | |
| 727 | /// A nonsettable system-wide clock that represents time since some | |
| 728 | /// unspecified point in the past. | |
| 729 | /// | |
| 730 | /// Monotonic: Guarantees that the time returned by consecutive calls | |
| 731 | /// will not go backwards, but successive calls may return identical | |
| 732 | /// (not-increased) time values. | |
| 733 | /// | |
| 734 | /// Not affected by discontinuous jumps in the system time (e.g., if | |
| 735 | /// the system administrator manually changes the clock), but may be | |
| 736 | /// affected by frequency adjustments. | |
| 737 | /// | |
| 738 | /// This clock expresses intent to **exclude time that the system is | |
| 739 | /// suspended**. However, implementations may be unable to satisify | |
| 740 | /// this, and may include that time. | |
| 741 | /// | |
| 742 | /// * On Linux, corresponds `CLOCK_MONOTONIC`. | |
| 743 | /// * On macOS, corresponds to `CLOCK_UPTIME_RAW`. | |
| 744 | awake, | |
| 745 | /// Identical to `awake` except it expresses intent to **include time | |
| 746 | /// that the system is suspended**, however, due to limitations it may | |
| 747 | /// behave identically to `awake`. | |
| 748 | /// | |
| 749 | /// * On Linux, corresponds `CLOCK_BOOTTIME`. | |
| 750 | /// * On macOS, corresponds to `CLOCK_MONOTONIC_RAW`. | |
| 751 | boot, | |
| 752 | /// Tracks the amount of CPU in user or kernel mode used by the calling | |
| 753 | /// process. | |
| 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 | } | |
| 708 | pub const Clock = enum { | |
| 709 | /// A settable system-wide clock that measures real (i.e. wall-clock) | |
| 710 | /// time. This clock is affected by discontinuous jumps in the system | |
| 711 | /// time (e.g., if the system administrator manually changes the | |
| 712 | /// clock), and by frequency adjust‐ ments performed by NTP and similar | |
| 713 | /// applications. | |
| 714 | /// | |
| 715 | /// This clock normally counts the number of seconds since 1970-01-01 | |
| 716 | /// 00:00:00 Coordinated Universal Time (UTC) except that it ignores | |
| 717 | /// leap seconds; near a leap second it is typically adjusted by NTP to | |
| 718 | /// stay roughly in sync with UTC. | |
| 719 | /// | |
| 720 | /// The epoch is implementation-defined. For example NTFS/Windows uses | |
| 721 | /// 1601-01-01. | |
| 722 | real, | |
| 723 | /// A nonsettable system-wide clock that represents time since some | |
| 724 | /// unspecified point in the past. | |
| 725 | /// | |
| 726 | /// Monotonic: Guarantees that the time returned by consecutive calls | |
| 727 | /// will not go backwards, but successive calls may return identical | |
| 728 | /// (not-increased) time values. | |
| 729 | /// | |
| 730 | /// Not affected by discontinuous jumps in the system time (e.g., if | |
| 731 | /// the system administrator manually changes the clock), but may be | |
| 732 | /// affected by frequency adjustments. | |
| 733 | /// | |
| 734 | /// This clock expresses intent to **exclude time that the system is | |
| 735 | /// suspended**. However, implementations may be unable to satisify | |
| 736 | /// this, and may include that time. | |
| 737 | /// | |
| 738 | /// * On Linux, corresponds `CLOCK_MONOTONIC`. | |
| 739 | /// * On macOS, corresponds to `CLOCK_UPTIME_RAW`. | |
| 740 | awake, | |
| 741 | /// Identical to `awake` except it expresses intent to **include time | |
| 742 | /// that the system is suspended**, however, due to limitations it may | |
| 743 | /// behave identically to `awake`. | |
| 744 | /// | |
| 745 | /// * On Linux, corresponds `CLOCK_BOOTTIME`. | |
| 746 | /// * On macOS, corresponds to `CLOCK_MONOTONIC_RAW`. | |
| 747 | boot, | |
| 748 | /// Tracks the amount of CPU in user or kernel mode used by the calling | |
| 749 | /// process. | |
| 750 | cpu_process, | |
| 751 | /// Tracks the amount of CPU in user or kernel mode used by the calling | |
| 752 | /// thread. | |
| 753 | cpu_thread, | |
| 771 | 754 | |
| 772 | 755 | pub const Error = error{UnsupportedClock} || UnexpectedError; |
| 773 | 756 | |
| 774 | 757 | /// This function is not cancelable because first of all it does not block, |
| 775 | 758 | /// but more importantly, the cancelation logic itself may want to check |
| 776 | 759 | /// the time. |
| 777 | pub fn now(io: Io, clock: Clock) Error!Timestamp { | |
| 778 | return .{ | |
| 779 | .nanoseconds = try io.vtable.now(io.userdata, clock), | |
| 780 | .clock = clock, | |
| 781 | }; | |
| 760 | pub fn now(clock: Clock, io: Io) Error!Io.Timestamp { | |
| 761 | return io.vtable.now(io.userdata, clock); | |
| 782 | 762 | } |
| 783 | 763 | |
| 784 | pub fn fromNow(io: Io, clock: Clock, duration: Duration) Error!Timestamp { | |
| 785 | const now_ts = try now(io, clock); | |
| 786 | return addDuration(now_ts, duration); | |
| 787 | } | |
| 764 | pub const Timestamp = struct { | |
| 765 | raw: Io.Timestamp, | |
| 766 | clock: Clock, | |
| 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 | } | |
| 788 | 777 | |
| 789 | pub fn untilNow(timestamp: Timestamp, io: Io) Error!Duration { | |
| 790 | const now_ts = try Timestamp.now(io, timestamp.clock); | |
| 791 | return timestamp.durationTo(now_ts); | |
| 792 | } | |
| 778 | pub fn wait(t: Clock.Timestamp, io: Io) SleepError!void { | |
| 779 | return io.vtable.sleep(io.userdata, .{ .deadline = t }); | |
| 780 | } | |
| 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 | } | |
| 793 | 789 | |
| 794 | pub fn durationFromNow(timestamp: Timestamp, io: Io) Error!Duration { | |
| 795 | const now_ts = try now(io, timestamp.clock); | |
| 796 | return now_ts.durationTo(timestamp); | |
| 790 | pub fn addDuration(from: Clock.Timestamp, duration: Clock.Duration) Clock.Timestamp { | |
| 791 | assert(from.clock == duration.clock); | |
| 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 | ||
| 845 | pub 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 | } |
| 798 | 853 | |
| 799 | pub fn toClock(t: Timestamp, io: Io, clock: Clock) Error!Timestamp { | |
| 800 | if (t.clock == clock) return t; | |
| 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); | |
| 854 | pub fn addDuration(from: Timestamp, duration: Duration) Timestamp { | |
| 855 | return .{ .nanoseconds = from.nanoseconds + duration.nanoseconds }; | |
| 805 | 856 | } |
| 806 | 857 | |
| 807 | pub fn compare(lhs: Timestamp, op: std.math.CompareOperator, rhs: Timestamp) bool { | |
| 808 | assert(lhs.clock == rhs.clock); | |
| 809 | return std.math.compare(lhs.nanoseconds, op, rhs.nanoseconds); | |
| 858 | pub fn withClock(t: Timestamp, clock: Clock) Clock.Timestamp { | |
| 859 | return .{ .nanoseconds = t.nanoseconds, .clock = clock }; | |
| 810 | 860 | } |
| 811 | 861 | |
| 812 | 862 | pub fn toSeconds(t: Timestamp) i64 { |
| 813 | 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 | }; |
| 816 | 875 | |
| 817 | 876 | pub const Duration = struct { |
| 818 | 877 | nanoseconds: i96, |
| 819 | 878 | |
| 879 | pub const zero: Duration = .{ .nanoseconds = 0 }; | |
| 820 | 880 | pub const max: Duration = .{ .nanoseconds = std.math.maxInt(i96) }; |
| 821 | 881 | |
| 822 | 882 | pub fn fromNanoseconds(x: i96) Duration { |
| ... | ... | @@ -842,38 +902,29 @@ pub const Duration = struct { |
| 842 | 902 | pub fn toNanoseconds(d: Duration) i96 { |
| 843 | 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 | }; |
| 850 | 906 | |
| 851 | 907 | /// Declares under what conditions an operation should return `error.Timeout`. |
| 852 | 908 | pub const Timeout = union(enum) { |
| 853 | 909 | none, |
| 854 | duration: ClockAndDuration, | |
| 855 | deadline: Timestamp, | |
| 910 | duration: Clock.Duration, | |
| 911 | deadline: Clock.Timestamp, | |
| 856 | 912 | |
| 857 | 913 | pub const Error = error{ Timeout, UnsupportedClock }; |
| 858 | 914 | |
| 859 | pub const ClockAndDuration = struct { | |
| 860 | clock: Timestamp.Clock, | |
| 861 | duration: Duration, | |
| 862 | }; | |
| 863 | ||
| 864 | pub fn toDeadline(t: Timeout, io: Io) Timestamp.Error!?Timestamp { | |
| 915 | pub fn toDeadline(t: Timeout, io: Io) Clock.Error!?Clock.Timestamp { | |
| 865 | 916 | return switch (t) { |
| 866 | 917 | .none => null, |
| 867 | .duration => |d| try .fromNow(io, d.clock, d.duration), | |
| 918 | .duration => |d| try .fromNow(io, d), | |
| 868 | 919 | .deadline => |d| d, |
| 869 | 920 | }; |
| 870 | 921 | } |
| 871 | 922 | |
| 872 | pub fn toDurationFromNow(t: Timeout, io: Io) Timestamp.Error!?ClockAndDuration { | |
| 923 | pub fn toDurationFromNow(t: Timeout, io: Io) Clock.Error!?Clock.Duration { | |
| 873 | 924 | return switch (t) { |
| 874 | 925 | .none => null, |
| 875 | 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 | } |
| 879 | 930 |
lib/std/Io/Dir.zig+1-1| ... | ... | @@ -85,7 +85,7 @@ pub fn updateFile( |
| 85 | 85 | }; |
| 86 | 86 | |
| 87 | 87 | if (src_stat.size == dest_stat.size and |
| 88 | src_stat.mtime == dest_stat.mtime and | |
| 88 | src_stat.mtime.nanoseconds == dest_stat.mtime.nanoseconds and | |
| 89 | 89 | actual_mode == dest_stat.mode) |
| 90 | 90 | { |
| 91 | 91 | return .fresh; |
lib/std/Io/File.zig+3-7| ... | ... | @@ -45,16 +45,12 @@ pub const Stat = struct { |
| 45 | 45 | /// This is available on POSIX systems and is always 0 otherwise. |
| 46 | 46 | mode: Mode, |
| 47 | 47 | kind: Kind, |
| 48 | ||
| 49 | 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 clock | |
| 51 | atime: i128, | |
| 49 | atime: Io.Timestamp, | |
| 52 | 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 clock | |
| 54 | mtime: i128, | |
| 51 | mtime: Io.Timestamp, | |
| 55 | 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 clock | |
| 57 | ctime: i128, | |
| 53 | ctime: Io.Timestamp, | |
| 58 | 54 | }; |
| 59 | 55 | |
| 60 | 56 | pub 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 | 1147 | }; |
| 1148 | 1148 | } |
| 1149 | 1149 | |
| 1150 | fn nowPosix(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 { | |
| 1150 | fn nowPosix(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | |
| 1151 | 1151 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1152 | 1152 | _ = pool; |
| 1153 | 1153 | const clock_id: posix.clockid_t = clockToPosix(clock); |
| 1154 | 1154 | var tp: posix.timespec = undefined; |
| 1155 | 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 | 1157 | .INVAL => return error.UnsupportedClock, |
| 1158 | 1158 | else => |err| return posix.unexpectedErrno(err), |
| 1159 | 1159 | } |
| 1160 | 1160 | } |
| 1161 | 1161 | |
| 1162 | fn nowWindows(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 { | |
| 1162 | fn nowWindows(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | |
| 1163 | 1163 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1164 | 1164 | _ = pool; |
| 1165 | 1165 | switch (clock) { |
| 1166 | 1166 | .realtime => { |
| 1167 | 1167 | // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds |
| 1168 | 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 | 1171 | .monotonic, .uptime => { |
| 1172 | 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 | 1178 | } |
| 1179 | 1179 | } |
| 1180 | 1180 | |
| 1181 | fn nowWasi(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 { | |
| 1181 | fn nowWasi(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | |
| 1182 | 1182 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1183 | 1183 | _ = pool; |
| 1184 | 1184 | var ns: std.os.wasi.timestamp_t = undefined; |
| ... | ... | @@ -1196,13 +1196,10 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { |
| 1196 | 1196 | }); |
| 1197 | 1197 | const deadline_nanoseconds: i96 = switch (timeout) { |
| 1198 | 1198 | .none => std.math.maxInt(i96), |
| 1199 | .duration => |d| d.duration.nanoseconds, | |
| 1200 | .deadline => |deadline| deadline.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)), | |
| 1199 | .duration => |duration| duration.raw.nanoseconds, | |
| 1200 | .deadline => |deadline| deadline.raw.nanoseconds, | |
| 1205 | 1201 | }; |
| 1202 | var timespec: posix.timespec = timestampToPosix(deadline_nanoseconds); | |
| 1206 | 1203 | while (true) { |
| 1207 | 1204 | try pool.checkCancel(); |
| 1208 | 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 | 1264 | .sec = std.math.maxInt(sec_type), |
| 1268 | 1265 | .nsec = std.math.maxInt(nsec_type), |
| 1269 | 1266 | }; |
| 1270 | const ns = 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 | }; | |
| 1267 | break :t timestampToPosix(d.duration.nanoseconds); | |
| 1275 | 1268 | }; |
| 1276 | 1269 | while (true) { |
| 1277 | 1270 | try pool.checkCancel(); |
| ... | ... | @@ -1879,8 +1872,8 @@ fn netReceive( |
| 1879 | 1872 | const max_poll_ms = std.math.maxInt(u31); |
| 1880 | 1873 | const timeout_ms: u31 = if (deadline) |d| t: { |
| 1881 | 1874 | const duration = d.durationFromNow(pool.io()) catch |err| return .{ err, message_i }; |
| 1882 | if (duration.nanoseconds <= 0) return .{ error.Timeout, message_i }; | |
| 1883 | break :t @intCast(@min(max_poll_ms, duration.toMilliseconds())); | |
| 1875 | if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i }; | |
| 1876 | break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds())); | |
| 1884 | 1877 | } else max_poll_ms; |
| 1885 | 1878 | |
| 1886 | 1879 | const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms); |
| ... | ... | @@ -2160,7 +2153,7 @@ fn recoverableOsBugDetected() void { |
| 2160 | 2153 | if (builtin.mode == .Debug) unreachable; |
| 2161 | 2154 | } |
| 2162 | 2155 | |
| 2163 | fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t { | |
| 2156 | fn clockToPosix(clock: Io.Clock) posix.clockid_t { | |
| 2164 | 2157 | return switch (clock) { |
| 2165 | 2158 | .real => posix.CLOCK.REALTIME, |
| 2166 | 2159 | .awake => switch (builtin.os.tag) { |
| ... | ... | @@ -2176,7 +2169,7 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t { |
| 2176 | 2169 | }; |
| 2177 | 2170 | } |
| 2178 | 2171 | |
| 2179 | fn clockToWasi(clock: Io.Timestamp.Clock) std.os.wasi.clockid_t { | |
| 2172 | fn clockToWasi(clock: Io.Clock) std.os.wasi.clockid_t { | |
| 2180 | 2173 | return switch (clock) { |
| 2181 | 2174 | .realtime => .REALTIME, |
| 2182 | 2175 | .awake => .MONOTONIC, |
| ... | ... | @@ -2204,9 +2197,9 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.File.Stat { |
| 2204 | 2197 | std.os.linux.S.IFSOCK => .unix_domain_socket, |
| 2205 | 2198 | else => .unknown, |
| 2206 | 2199 | }, |
| 2207 | .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec, | |
| 2208 | .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec, | |
| 2209 | .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec, | |
| 2200 | .atime = .{ .nanoseconds = @intCast(@as(i128, atime.sec) * std.time.ns_per_s + atime.nsec) }, | |
| 2201 | .mtime = .{ .nanoseconds = @intCast(@as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec) }, | |
| 2202 | .ctime = .{ .nanoseconds = @intCast(@as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec) }, | |
| 2210 | 2203 | }; |
| 2211 | 2204 | } |
| 2212 | 2205 | |
| ... | ... | @@ -2238,9 +2231,9 @@ fn statFromPosix(st: *const std.posix.Stat) Io.File.Stat { |
| 2238 | 2231 | |
| 2239 | 2232 | break :k .unknown; |
| 2240 | 2233 | }, |
| 2241 | .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec, | |
| 2242 | .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec, | |
| 2243 | .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec, | |
| 2234 | .atime = timestampFromPosix(&atime), | |
| 2235 | .mtime = timestampFromPosix(&mtime), | |
| 2236 | .ctime = timestampFromPosix(&ctime), | |
| 2244 | 2237 | }; |
| 2245 | 2238 | } |
| 2246 | 2239 | |
| ... | ... | @@ -2263,3 +2256,14 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat { |
| 2263 | 2256 | .ctime = st.ctim, |
| 2264 | 2257 | }; |
| 2265 | 2258 | } |
| 2259 | ||
| 2260 | fn 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 | ||
| 2264 | fn 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 | 79 | NameServerFailure, |
| 80 | 80 | /// Failed to open or read "/etc/hosts" or "/etc/resolv.conf". |
| 81 | 81 | DetectingNetworkConfigurationFailed, |
| 82 | } || Io.Timestamp.Error || IpAddress.BindError || Io.Cancelable; | |
| 82 | } || Io.Clock.Error || IpAddress.BindError || Io.Cancelable; | |
| 83 | 83 | |
| 84 | 84 | pub const LookupResult = struct { |
| 85 | 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 | 294 | |
| 295 | 295 | // boot clock is chosen because time the computer is suspended should count |
| 296 | 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 | 299 | const final_ts = now_ts.addDuration(.fromSeconds(rc.timeout_seconds)); |
| 299 | 300 | const attempt_duration: Io.Duration = .{ |
| 300 | 301 | .nanoseconds = std.time.ns_per_s * @as(usize, rc.timeout_seconds) / rc.attempts, |
| 301 | 302 | }; |
| 302 | 303 | |
| 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 | 305 | const max_messages = queries_buffer.len * ResolvConf.max_nameservers; |
| 305 | 306 | { |
| 306 | 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 | 320 | _ = io.vtable.netSend(io.userdata, socket.handle, message_buffer[0..message_i], .{}); |
| 320 | 321 | } |
| 321 | 322 | |
| 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 | } }; | |
| 323 | 327 | |
| 324 | 328 | while (true) { |
| 325 | 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 | 637 | pub fn updateTimes( |
| 638 | 638 | self: File, |
| 639 | 639 | /// access timestamp in nanoseconds |
| 640 | atime: i128, | |
| 640 | atime: Io.Timestamp, | |
| 641 | 641 | /// last modification timestamp in nanoseconds |
| 642 | mtime: i128, | |
| 642 | mtime: Io.Timestamp, | |
| 643 | 643 | ) UpdateTimesError!void { |
| 644 | 644 | if (builtin.os.tag == .windows) { |
| 645 | const atime_ft = windows.nanoSecondsToFileTime(atime); | |
| 646 | const mtime_ft = windows.nanoSecondsToFileTime(mtime); | |
| 645 | const atime_ft = windows.nanoSecondsToFileTime(atime.nanoseconds); | |
| 646 | const mtime_ft = windows.nanoSecondsToFileTime(mtime.nanoseconds); | |
| 647 | 647 | return windows.SetFileTime(self.handle, null, &atime_ft, &mtime_ft); |
| 648 | 648 | } |
| 649 | 649 | const times = [2]posix.timespec{ |
| 650 | 650 | posix.timespec{ |
| 651 | .sec = math.cast(isize, @divFloor(atime, std.time.ns_per_s)) orelse maxInt(isize), | |
| 652 | .nsec = math.cast(isize, @mod(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.nanoseconds, std.time.ns_per_s)) orelse maxInt(isize), | |
| 653 | 653 | }, |
| 654 | 654 | posix.timespec{ |
| 655 | .sec = math.cast(isize, @divFloor(mtime, std.time.ns_per_s)) orelse maxInt(isize), | |
| 656 | .nsec = math.cast(isize, @mod(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.nanoseconds, std.time.ns_per_s)) orelse maxInt(isize), | |
| 657 | 657 | }, |
| 658 | 658 | }; |
| 659 | 659 | try posix.futimens(self.handle, &times); |
lib/std/http/Client.zig+1-1| ... | ... | @@ -320,7 +320,7 @@ pub const Connection = struct { |
| 320 | 320 | const tls: *Tls = @ptrCast(base); |
| 321 | 321 | var random_buffer: [176]u8 = undefined; |
| 322 | 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 | 324 | tls.* = .{ |
| 325 | 325 | .connection = .{ |
| 326 | 326 | .client = client, |
lib/std/tar/Writer.zig+4-4| ... | ... | @@ -18,7 +18,6 @@ pub const Options = struct { |
| 18 | 18 | |
| 19 | 19 | underlying_writer: *Io.Writer, |
| 20 | 20 | prefix: []const u8 = "", |
| 21 | mtime_now: u64 = 0, | |
| 22 | 21 | |
| 23 | 22 | const Error = error{ |
| 24 | 23 | WriteFailed, |
| ... | ... | @@ -44,10 +43,12 @@ pub fn writeFile( |
| 44 | 43 | w: *Writer, |
| 45 | 44 | sub_path: []const u8, |
| 46 | 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 | 50 | ) WriteFileError!void { |
| 49 | 51 | const size = try file_reader.getSize(); |
| 50 | const mtime: u64 = @intCast(@divFloor(stat_mtime, std.time.ns_per_s)); | |
| 51 | 52 | |
| 52 | 53 | var header: Header = .{}; |
| 53 | 54 | try w.setPath(&header, sub_path); |
| ... | ... | @@ -238,7 +239,6 @@ pub const Header = extern struct { |
| 238 | 239 | } |
| 239 | 240 | |
| 240 | 241 | // Integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time. |
| 241 | // mtime == 0 will use current time | |
| 242 | 242 | pub fn setMtime(w: *Header, mtime: u64) error{OctalOverflow}!void { |
| 243 | 243 | try octal(&w.mtime, mtime); |
| 244 | 244 | } |