| ... | @@ -135,7 +135,7 @@ pub const Instant = struct { | ... | @@ -135,7 +135,7 @@ pub const Instant = struct { |
| 135 | const clock_id = switch (builtin.os.tag) { | 135 | const clock_id = switch (builtin.os.tag) { |
| 136 | .windows => { | 136 | .windows => { |
| 137 | // QPC on windows doesn't fail on >= XP/2000 and includes time suspended. | 137 | // QPC on windows doesn't fail on >= XP/2000 and includes time suspended. |
| 138 | return Instant{ .timestamp = windows.QueryPerformanceCounter() }; | 138 | return .{ .timestamp = windows.QueryPerformanceCounter() }; |
| 139 | }, | 139 | }, |
| 140 | .wasi => { | 140 | .wasi => { |
| 141 | var ns: std.os.wasi.timestamp_t = undefined; | 141 | var ns: std.os.wasi.timestamp_t = undefined; |
| ... | @@ -147,7 +147,7 @@ pub const Instant = struct { | ... | @@ -147,7 +147,7 @@ pub const Instant = struct { |
| 147 | var value: std.os.uefi.Time = undefined; | 147 | var value: std.os.uefi.Time = undefined; |
| 148 | const status = std.os.uefi.system_table.runtime_services.getTime(&value, null); | 148 | const status = std.os.uefi.system_table.runtime_services.getTime(&value, null); |
| 149 | if (status != .success) return error.Unsupported; | 149 | if (status != .success) return error.Unsupported; |
| 150 | return Instant{ .timestamp = value.toEpoch() }; | 150 | return .{ .timestamp = value.toEpoch() }; |
| 151 | }, | 151 | }, |
| 152 | // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while | 152 | // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while |
| 153 | // suspended. | 153 | // suspended. |
| ... | @@ -185,36 +185,38 @@ pub const Instant = struct { | ... | @@ -185,36 +185,38 @@ pub const Instant = struct { |
| 185 | /// This assumes that the `earlier` Instant represents a moment in time before or equal to `self`. | 185 | /// This assumes that the `earlier` Instant represents a moment in time before or equal to `self`. |
| 186 | /// This also assumes that the time that has passed between both Instants fits inside a u64 (~585 yrs). | 186 | /// This also assumes that the time that has passed between both Instants fits inside a u64 (~585 yrs). |
| 187 | pub fn since(self: Instant, earlier: Instant) u64 { | 187 | pub fn since(self: Instant, earlier: Instant) u64 { |
| 188 | if (builtin.os.tag == .windows) { | 188 | switch (builtin.os.tag) { |
| 189 | // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA | 189 | .windows => { |
| 190 | // (a read-only page of info updated and mapped by the kernel to all processes): | 190 | // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA |
| 191 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data | 191 | // (a read-only page of info updated and mapped by the kernel to all processes): |
| 192 | // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm | 192 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data |
| 193 | const qpc = self.timestamp - earlier.timestamp; | 193 | // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm |
| 194 | const qpf = windows.QueryPerformanceFrequency(); | 194 | const qpc = self.timestamp - earlier.timestamp; |
| 195 | | 195 | const qpf = windows.QueryPerformanceFrequency(); |
| 196 | // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it. | 196 | |
| 197 | // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701 | 197 | // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it. |
| 198 | const common_qpf = 10_000_000; | 198 | // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701 |
| 199 | if (qpf == common_qpf) { | 199 | const common_qpf = 10_000_000; |
| 200 | return qpc * (ns_per_s / common_qpf); | 200 | if (qpf == common_qpf) { |
| 201 | } | 201 | return qpc * (ns_per_s / common_qpf); |
| 202 | | 202 | } |
| 203 | // Convert to ns using fixed point. | 203 | |
| 204 | const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf)); | 204 | // Convert to ns using fixed point. |
| 205 | const result = (@as(u96, qpc) * scale) >> 32; | 205 | const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf)); |
| 206 | return @as(u64, @truncate(result)); | 206 | const result = (@as(u96, qpc) * scale) >> 32; |
| 207 | } | 207 | return @as(u64, @truncate(result)); |
| 208 | | 208 | }, |
| 209 | // WASI timestamps are directly in nanoseconds | 209 | .uefi, .wasi => { |
| 210 | if (builtin.os.tag == .wasi) { | 210 | // UEFI and WASI timestamps are directly in nanoseconds |
| 211 | return self.timestamp - earlier.timestamp; | 211 | return self.timestamp - earlier.timestamp; |
| | 212 | }, |
| | 213 | else => { |
| | 214 | // Convert timespec diff to ns |
| | 215 | const seconds = @as(u64, @intCast(self.timestamp.sec - earlier.timestamp.sec)); |
| | 216 | const elapsed = (seconds * ns_per_s) + @as(u32, @intCast(self.timestamp.nsec)); |
| | 217 | return elapsed - @as(u32, @intCast(earlier.timestamp.nsec)); |
| | 218 | }, |
| 212 | } | 219 | } |
| 213 | | | |
| 214 | // Convert timespec diff to ns | | |
| 215 | const seconds = @as(u64, @intCast(self.timestamp.sec - earlier.timestamp.sec)); | | |
| 216 | const elapsed = (seconds * ns_per_s) + @as(u32, @intCast(self.timestamp.nsec)); | | |
| 217 | return elapsed - @as(u32, @intCast(earlier.timestamp.nsec)); | | |
| 218 | } | 220 | } |
| 219 | }; | 221 | }; |
| 220 | | 222 | |