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