mirror of
https://github.com/ziglang/zig.git
synced 2025-12-15 18:53:07 +00:00
(breaking) std.time fixups and API changes
Remove the constants that assume a base unit in favor of explicit x_per_y constants. nanosecond calendar timestamps now use i128 for the type. This affects fs.File.Stat, std.time.nanoTimestamp, and fs.File.updateTimes. calendar timestamps are now signed, because the value can be less than the epoch (the user can set their computer time to whatever they wish). implement std.os.clock_gettime for Windows when clock id is CLOCK_CALENDAR.
This commit is contained in:
parent
c6e7d0fcfd
commit
53d011fa1a
@ -122,7 +122,7 @@ test "std.event.Batch" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sleepALittle(count: *usize) void {
|
fn sleepALittle(count: *usize) void {
|
||||||
std.time.sleep(1 * std.time.millisecond);
|
std.time.sleep(1 * std.time.ns_per_ms);
|
||||||
_ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
|
_ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -145,7 +145,7 @@ fn testGroup(allocator: *Allocator) callconv(.Async) void {
|
|||||||
testing.expectError(error.ItBroke, another.wait());
|
testing.expectError(error.ItBroke, another.wait());
|
||||||
}
|
}
|
||||||
fn sleepALittle(count: *usize) callconv(.Async) void {
|
fn sleepALittle(count: *usize) callconv(.Async) void {
|
||||||
std.time.sleep(1 * std.time.millisecond);
|
std.time.sleep(1 * std.time.ns_per_ms);
|
||||||
_ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
|
_ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
|
||||||
}
|
}
|
||||||
fn increaseByTen(count: *usize) callconv(.Async) void {
|
fn increaseByTen(count: *usize) callconv(.Async) void {
|
||||||
|
|||||||
@ -457,7 +457,7 @@ pub const Loop = struct {
|
|||||||
=> {
|
=> {
|
||||||
// Even poll() didn't work. The best we can do now is sleep for a
|
// Even poll() didn't work. The best we can do now is sleep for a
|
||||||
// small duration and then hope that something changed.
|
// small duration and then hope that something changed.
|
||||||
std.time.sleep(1 * std.time.millisecond);
|
std.time.sleep(1 * std.time.ns_per_ms);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
resume @frame();
|
resume @frame();
|
||||||
|
|||||||
@ -227,14 +227,12 @@ pub const File = struct {
|
|||||||
size: u64,
|
size: u64,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
|
||||||
/// access time in nanoseconds
|
/// Access time in nanoseconds, relative to UTC 1970-01-01.
|
||||||
atime: i64,
|
atime: i128,
|
||||||
|
/// Last modification time in nanoseconds, relative to UTC 1970-01-01.
|
||||||
/// last modification time in nanoseconds
|
mtime: i128,
|
||||||
mtime: i64,
|
/// Creation time in nanoseconds, relative to UTC 1970-01-01.
|
||||||
|
ctime: i128,
|
||||||
/// creation time in nanoseconds
|
|
||||||
ctime: i64,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const StatError = os.FStatError;
|
pub const StatError = os.FStatError;
|
||||||
@ -270,9 +268,9 @@ pub const File = struct {
|
|||||||
.inode = st.ino,
|
.inode = st.ino,
|
||||||
.size = @bitCast(u64, st.size),
|
.size = @bitCast(u64, st.size),
|
||||||
.mode = st.mode,
|
.mode = st.mode,
|
||||||
.atime = @as(i64, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
|
.atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
|
||||||
.mtime = @as(i64, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
|
.mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
|
||||||
.ctime = @as(i64, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
|
.ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,9 +284,9 @@ pub const File = struct {
|
|||||||
pub fn updateTimes(
|
pub fn updateTimes(
|
||||||
self: File,
|
self: File,
|
||||||
/// access timestamp in nanoseconds
|
/// access timestamp in nanoseconds
|
||||||
atime: i64,
|
atime: i128,
|
||||||
/// last modification timestamp in nanoseconds
|
/// last modification timestamp in nanoseconds
|
||||||
mtime: i64,
|
mtime: i128,
|
||||||
) UpdateTimesError!void {
|
) UpdateTimesError!void {
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
const atime_ft = windows.nanoSecondsToFileTime(atime);
|
const atime_ft = windows.nanoSecondsToFileTime(atime);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ test "openSelfExe" {
|
|||||||
self_exe_file.close();
|
self_exe_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond;
|
const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.ns_per_ms;
|
||||||
|
|
||||||
test "open file with exclusive nonblocking lock twice" {
|
test "open file with exclusive nonblocking lock twice" {
|
||||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||||
@ -142,8 +142,8 @@ const FileLockTestContext = struct {
|
|||||||
|
|
||||||
// Output variables
|
// Output variables
|
||||||
err: ?(File.OpenError || std.os.ReadError) = null,
|
err: ?(File.OpenError || std.os.ReadError) = null,
|
||||||
start_time: u64 = 0,
|
start_time: i64 = 0,
|
||||||
end_time: u64 = 0,
|
end_time: i64 = 0,
|
||||||
bytes_read: ?usize = null,
|
bytes_read: ?usize = null,
|
||||||
|
|
||||||
fn overlaps(self: *const @This(), other: *const @This()) bool {
|
fn overlaps(self: *const @This(), other: *const @This()) bool {
|
||||||
|
|||||||
@ -1135,13 +1135,13 @@ fn resMSendRc(
|
|||||||
}};
|
}};
|
||||||
const retry_interval = timeout / attempts;
|
const retry_interval = timeout / attempts;
|
||||||
var next: u32 = 0;
|
var next: u32 = 0;
|
||||||
var t2: u64 = std.time.milliTimestamp();
|
var t2: u64 = @bitCast(u64, std.time.milliTimestamp());
|
||||||
var t0 = t2;
|
var t0 = t2;
|
||||||
var t1 = t2 - retry_interval;
|
var t1 = t2 - retry_interval;
|
||||||
|
|
||||||
var servfail_retry: usize = undefined;
|
var servfail_retry: usize = undefined;
|
||||||
|
|
||||||
outer: while (t2 - t0 < timeout) : (t2 = std.time.milliTimestamp()) {
|
outer: while (t2 - t0 < timeout) : (t2 = @bitCast(u64, std.time.milliTimestamp())) {
|
||||||
if (t2 - t1 >= retry_interval) {
|
if (t2 - t1 >= retry_interval) {
|
||||||
// Query all configured nameservers in parallel
|
// Query all configured nameservers in parallel
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
|||||||
@ -3880,6 +3880,8 @@ pub fn dl_iterate_phdr(
|
|||||||
|
|
||||||
pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
|
pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
|
||||||
|
|
||||||
|
/// TODO: change this to return the timespec as a return value
|
||||||
|
/// TODO: look into making clk_id an enum
|
||||||
pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
|
pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
|
||||||
if (std.Target.current.os.tag == .wasi) {
|
if (std.Target.current.os.tag == .wasi) {
|
||||||
var ts: timestamp_t = undefined;
|
var ts: timestamp_t = undefined;
|
||||||
@ -3895,6 +3897,23 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (std.Target.current.os.tag == .windows) {
|
||||||
|
if (clk_id == CLOCK_REALTIME) {
|
||||||
|
var ft: windows.FILETIME = undefined;
|
||||||
|
windows.kernel32.GetSystemTimeAsFileTime(&ft);
|
||||||
|
// FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch.
|
||||||
|
const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||||
|
const ft_per_s = std.time.ns_per_s / 100;
|
||||||
|
tp.* = .{
|
||||||
|
.tv_sec = @intCast(i64, ft64 / ft_per_s) + std.time.epoch.windows,
|
||||||
|
.tv_nsec = @intCast(c_long, ft64 % ft_per_s) * 100,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// TODO POSIX implementation of CLOCK_MONOTONIC on Windows.
|
||||||
|
return error.UnsupportedClock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (errno(system.clock_gettime(clk_id, tp))) {
|
switch (errno(system.clock_gettime(clk_id, tp))) {
|
||||||
0 => return,
|
0 => return,
|
||||||
|
|||||||
@ -1456,3 +1456,12 @@ pub const POLLHUP = 0x010;
|
|||||||
pub const POLLNVAL = 0x020;
|
pub const POLLNVAL = 0x020;
|
||||||
|
|
||||||
pub const POLLSTANDARD = POLLIN | POLLPRI | POLLOUT | POLLRDNORM | POLLRDBAND | POLLWRBAND | POLLERR | POLLHUP | POLLNVAL;
|
pub const POLLSTANDARD = POLLIN | POLLPRI | POLLOUT | POLLRDNORM | POLLRDBAND | POLLWRBAND | POLLERR | POLLHUP | POLLNVAL;
|
||||||
|
|
||||||
|
pub const CLOCK_REALTIME = 0;
|
||||||
|
pub const CLOCK_MONOTONIC = 6;
|
||||||
|
pub const CLOCK_MONOTONIC_RAW = 4;
|
||||||
|
pub const CLOCK_MONOTONIC_RAW_APPROX = 5;
|
||||||
|
pub const CLOCK_UPTIME_RAW = 8;
|
||||||
|
pub const CLOCK_UPTIME_RAW_APPROX = 9;
|
||||||
|
pub const CLOCK_PROCESS_CPUTIME_ID = 12;
|
||||||
|
pub const CLOCK_THREAD_CPUTIME_ID = 16;
|
||||||
|
|||||||
@ -1193,23 +1193,23 @@ pub fn peb() *PEB {
|
|||||||
/// Universal Time (UTC).
|
/// Universal Time (UTC).
|
||||||
/// This function returns the number of nanoseconds since the canonical epoch,
|
/// This function returns the number of nanoseconds since the canonical epoch,
|
||||||
/// which is the POSIX one (Jan 01, 1970 AD).
|
/// which is the POSIX one (Jan 01, 1970 AD).
|
||||||
pub fn fromSysTime(hns: i64) i64 {
|
pub fn fromSysTime(hns: i64) i128 {
|
||||||
const adjusted_epoch = hns + std.time.epoch.windows * (std.time.ns_per_s / 100);
|
const adjusted_epoch = @as(i128, hns + std.time.epoch.windows) * (std.time.ns_per_s / 100);
|
||||||
return adjusted_epoch * 100;
|
return adjusted_epoch * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toSysTime(ns: i64) i64 {
|
pub fn toSysTime(ns: i128) i64 {
|
||||||
const hns = @divFloor(ns, 100);
|
const hns = @divFloor(ns, 100);
|
||||||
return hns - std.time.epoch.windows * (std.time.ns_per_s / 100);
|
return @intCast(i64, hns) - std.time.epoch.windows * (std.time.ns_per_s / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fileTimeToNanoSeconds(ft: FILETIME) i64 {
|
pub fn fileTimeToNanoSeconds(ft: FILETIME) i128 {
|
||||||
const hns = @bitCast(i64, (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime);
|
const hns = (@as(i64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||||
return fromSysTime(hns);
|
return fromSysTime(hns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
|
/// Converts a number of nanoseconds since the POSIX epoch to a Windows FILETIME.
|
||||||
pub fn nanoSecondsToFileTime(ns: i64) FILETIME {
|
pub fn nanoSecondsToFileTime(ns: i128) FILETIME {
|
||||||
const adjusted = @bitCast(u64, toSysTime(ns));
|
const adjusted = @bitCast(u64, toSysTime(ns));
|
||||||
return FILETIME{
|
return FILETIME{
|
||||||
.dwHighDateTime = @truncate(u32, adjusted >> 32),
|
.dwHighDateTime = @truncate(u32, adjusted >> 32),
|
||||||
|
|||||||
@ -31,10 +31,10 @@ pub const Progress = struct {
|
|||||||
output_buffer: [100]u8 = undefined,
|
output_buffer: [100]u8 = undefined,
|
||||||
|
|
||||||
/// How many nanoseconds between writing updates to the terminal.
|
/// How many nanoseconds between writing updates to the terminal.
|
||||||
refresh_rate_ns: u64 = 50 * std.time.millisecond,
|
refresh_rate_ns: u64 = 50 * std.time.ns_per_ms,
|
||||||
|
|
||||||
/// How many nanoseconds to keep the output hidden
|
/// How many nanoseconds to keep the output hidden
|
||||||
initial_delay_ns: u64 = 500 * std.time.millisecond,
|
initial_delay_ns: u64 = 500 * std.time.ns_per_ms,
|
||||||
|
|
||||||
done: bool = true,
|
done: bool = true,
|
||||||
|
|
||||||
@ -282,24 +282,24 @@ test "basic functionality" {
|
|||||||
next_sub_task = (next_sub_task + 1) % sub_task_names.len;
|
next_sub_task = (next_sub_task + 1) % sub_task_names.len;
|
||||||
|
|
||||||
node.completeOne();
|
node.completeOne();
|
||||||
std.time.sleep(5 * std.time.millisecond);
|
std.time.sleep(5 * std.time.ns_per_ms);
|
||||||
node.completeOne();
|
node.completeOne();
|
||||||
node.completeOne();
|
node.completeOne();
|
||||||
std.time.sleep(5 * std.time.millisecond);
|
std.time.sleep(5 * std.time.ns_per_ms);
|
||||||
node.completeOne();
|
node.completeOne();
|
||||||
node.completeOne();
|
node.completeOne();
|
||||||
std.time.sleep(5 * std.time.millisecond);
|
std.time.sleep(5 * std.time.ns_per_ms);
|
||||||
|
|
||||||
node.end();
|
node.end();
|
||||||
|
|
||||||
std.time.sleep(5 * std.time.millisecond);
|
std.time.sleep(5 * std.time.ns_per_ms);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", null);
|
var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", null);
|
||||||
node.activate();
|
node.activate();
|
||||||
std.time.sleep(10 * std.time.millisecond);
|
std.time.sleep(10 * std.time.ns_per_ms);
|
||||||
progress.refresh();
|
progress.refresh();
|
||||||
std.time.sleep(10 * std.time.millisecond);
|
std.time.sleep(10 * std.time.ns_per_ms);
|
||||||
node.end();
|
node.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -152,15 +152,15 @@ const PosixEvent = struct {
|
|||||||
if (comptime std.Target.current.isDarwin()) {
|
if (comptime std.Target.current.isDarwin()) {
|
||||||
var tv: os.darwin.timeval = undefined;
|
var tv: os.darwin.timeval = undefined;
|
||||||
assert(os.darwin.gettimeofday(&tv, null) == 0);
|
assert(os.darwin.gettimeofday(&tv, null) == 0);
|
||||||
timeout_abs += @intCast(u64, tv.tv_sec) * time.second;
|
timeout_abs += @intCast(u64, tv.tv_sec) * time.ns_per_s;
|
||||||
timeout_abs += @intCast(u64, tv.tv_usec) * time.microsecond;
|
timeout_abs += @intCast(u64, tv.tv_usec) * time.us_per_s;
|
||||||
} else {
|
} else {
|
||||||
os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable;
|
os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable;
|
||||||
timeout_abs += @intCast(u64, ts.tv_sec) * time.second;
|
timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s;
|
||||||
timeout_abs += @intCast(u64, ts.tv_nsec);
|
timeout_abs += @intCast(u64, ts.tv_nsec);
|
||||||
}
|
}
|
||||||
ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.second));
|
ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s));
|
||||||
ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.second));
|
ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s));
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!self.is_set) {
|
while (!self.is_set) {
|
||||||
|
|||||||
190
lib/std/time.zig
190
lib/std/time.zig
@ -4,16 +4,14 @@ const assert = std.debug.assert;
|
|||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const os = std.os;
|
const os = std.os;
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
|
const is_windows = std.Target.current.os.tag == .windows;
|
||||||
|
|
||||||
pub const epoch = @import("time/epoch.zig");
|
pub const epoch = @import("time/epoch.zig");
|
||||||
|
|
||||||
const is_windows = std.Target.current.os.tag == .windows;
|
|
||||||
|
|
||||||
/// Spurious wakeups are possible and no precision of timing is guaranteed.
|
/// Spurious wakeups are possible and no precision of timing is guaranteed.
|
||||||
/// TODO integrate with evented I/O
|
/// TODO integrate with evented I/O
|
||||||
pub fn sleep(nanoseconds: u64) void {
|
pub fn sleep(nanoseconds: u64) void {
|
||||||
if (is_windows) {
|
if (is_windows) {
|
||||||
const ns_per_ms = ns_per_s / ms_per_s;
|
|
||||||
const big_ms_from_ns = nanoseconds / ns_per_ms;
|
const big_ms_from_ns = nanoseconds / ns_per_ms;
|
||||||
const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD);
|
const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD);
|
||||||
os.windows.kernel32.Sleep(ms);
|
os.windows.kernel32.Sleep(ms);
|
||||||
@ -49,105 +47,78 @@ pub fn sleep(nanoseconds: u64) void {
|
|||||||
std.os.nanosleep(s, ns);
|
std.os.nanosleep(s, ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the posix timestamp, UTC, in seconds
|
/// Get a calendar timestamp, in seconds, relative to UTC 1970-01-01.
|
||||||
/// TODO audit this function. is it possible to return an error?
|
/// Precision of timing depends on the hardware and operating system.
|
||||||
pub fn timestamp() u64 {
|
/// The return value is signed because it is possible to have a date that is
|
||||||
return @divFloor(milliTimestamp(), ms_per_s);
|
/// before the epoch.
|
||||||
|
/// See `std.os.clock_gettime` for a POSIX timestamp.
|
||||||
|
pub fn timestamp() i64 {
|
||||||
|
return @divFloor(milliTimestamp(), ns_per_s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the posix timestamp, UTC, in milliseconds
|
/// Get a calendar timestamp, in milliseconds, relative to UTC 1970-01-01.
|
||||||
/// TODO audit this function. is it possible to return an error?
|
/// Precision of timing depends on the hardware and operating system.
|
||||||
pub fn milliTimestamp() u64 {
|
/// The return value is signed because it is possible to have a date that is
|
||||||
return @divFloor(nanoTimestamp(), millisecond);
|
/// before the epoch.
|
||||||
|
/// See `std.os.clock_gettime` for a POSIX timestamp.
|
||||||
|
pub fn milliTimestamp() i64 {
|
||||||
|
return @intCast(i64, @divFloor(nanoTimestamp(), ns_per_ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
const DarwinTimeStart = struct {
|
/// Get a calendar timestamp, in nanoseconds, relative to UTC 1970-01-01.
|
||||||
timebase: os.darwin.mach_timebase_info_data,
|
/// Precision of timing depends on the hardware and operating system.
|
||||||
inittime: os.darwin.timespec,
|
/// On Windows this has a maximum granularity of 100 nanoseconds.
|
||||||
initclock: u64,
|
/// The return value is signed because it is possible to have a date that is
|
||||||
};
|
/// before the epoch.
|
||||||
|
/// See `std.os.clock_gettime` for a POSIX timestamp.
|
||||||
var global_timestart: DarwinTimeStart = undefined;
|
pub fn nanoTimestamp() i128 {
|
||||||
var init_global_timestart_once = std.once(init_global_timestart);
|
|
||||||
|
|
||||||
pub fn init_global_timestart() void {
|
|
||||||
var micro: os.darwin.timeval = undefined;
|
|
||||||
var timestart: DarwinTimeStart = undefined;
|
|
||||||
|
|
||||||
os.darwin.mach_timebase_info(×tart.timebase);
|
|
||||||
|
|
||||||
const err = os.darwin.gettimeofday(µ, null);
|
|
||||||
assert(err == 0);
|
|
||||||
|
|
||||||
timestart.initclock = os.darwin.mach_absolute_time();
|
|
||||||
timestart.inittime.tv_sec = micro.tv_sec;
|
|
||||||
timestart.inittime.tv_nsec = micro.tv_usec * 1000;
|
|
||||||
|
|
||||||
global_timestart = timestart;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the posix timestamp, UTC, in nanoseconds
|
|
||||||
///
|
|
||||||
/// On windows this only has a granularity of 100 nanoseconds.
|
|
||||||
///
|
|
||||||
/// TODO audit this function. is it possible to return an error?
|
|
||||||
pub fn nanoTimestamp() u64 {
|
|
||||||
if (is_windows) {
|
if (is_windows) {
|
||||||
//FileTime has a granularity of 100 nanoseconds
|
// FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch,
|
||||||
// and uses the NTFS/Windows epoch
|
// which is 1601-01-01.
|
||||||
|
const epoch_adj = epoch.windows * (ns_per_s / 100);
|
||||||
var ft: os.windows.FILETIME = undefined;
|
var ft: os.windows.FILETIME = undefined;
|
||||||
os.windows.kernel32.GetSystemTimeAsFileTime(&ft);
|
os.windows.kernel32.GetSystemTimeAsFileTime(&ft);
|
||||||
const ns_per_hns = 100;
|
|
||||||
const epoch_adj = epoch.windows * ns_per_s;
|
|
||||||
|
|
||||||
const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||||
return (ft64 * ns_per_hns) - -epoch_adj;
|
return @as(i128, @bitCast(i64, ft64) + epoch_adj) * 100;
|
||||||
}
|
}
|
||||||
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
if (builtin.os.tag == .wasi and !builtin.link_libc) {
|
||||||
var ns: os.wasi.timestamp_t = undefined;
|
var ns: os.wasi.timestamp_t = undefined;
|
||||||
|
|
||||||
// TODO: Verify that precision is ignored
|
|
||||||
const err = os.wasi.clock_time_get(os.wasi.CLOCK_REALTIME, 1, &ns);
|
const err = os.wasi.clock_time_get(os.wasi.CLOCK_REALTIME, 1, &ns);
|
||||||
assert(err == os.wasi.ESUCCESS);
|
assert(err == os.wasi.ESUCCESS);
|
||||||
|
|
||||||
return ns;
|
return ns;
|
||||||
}
|
}
|
||||||
if (comptime std.Target.current.isDarwin()) {
|
|
||||||
// https://stackoverflow.com/a/21352348
|
|
||||||
init_global_timestart_once.call();
|
|
||||||
|
|
||||||
const clock: u64 = os.darwin.mach_absolute_time() - global_timestart.initclock;
|
|
||||||
const nano = @divFloor(clock * @as(u64, global_timestart.timebase.numer), @as(u64, global_timestart.timebase.denom));
|
|
||||||
|
|
||||||
const tv_sec_nsec = @intCast(u64, global_timestart.inittime.tv_sec) * ns_per_s;
|
|
||||||
const tv_nsec = @intCast(u64, global_timestart.inittime.tv_nsec);
|
|
||||||
|
|
||||||
return tv_sec_nsec + tv_nsec + nano;
|
|
||||||
}
|
|
||||||
var ts: os.timespec = undefined;
|
var ts: os.timespec = undefined;
|
||||||
//From what I can tell there's no reason clock_gettime
|
os.clock_gettime(os.CLOCK_REALTIME, &ts) catch |err| switch (err) {
|
||||||
// should ever fail for us with CLOCK_REALTIME,
|
error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS".
|
||||||
// seccomp aside.
|
};
|
||||||
os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable;
|
return (@as(i128, ts.tv_sec) * ns_per_s) + ts.tv_nsec;
|
||||||
const sec_ns = @intCast(u64, ts.tv_sec) * ns_per_s;
|
|
||||||
return sec_ns + @intCast(u64, ts.tv_nsec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multiples of a base unit (nanoseconds)
|
// Divisions of a nanosecond.
|
||||||
pub const nanosecond = 1;
|
pub const ns_per_us = 1000;
|
||||||
pub const microsecond = 1000 * nanosecond;
|
pub const ns_per_ms = 1000 * ns_per_us;
|
||||||
pub const millisecond = 1000 * microsecond;
|
pub const ns_per_s = 1000 * ns_per_ms;
|
||||||
pub const second = 1000 * millisecond;
|
pub const ns_per_min = 60 * ns_per_s;
|
||||||
pub const minute = 60 * second;
|
pub const ns_per_hour = 60 * ns_per_min;
|
||||||
pub const hour = 60 * minute;
|
pub const ns_per_day = 24 * ns_per_hour;
|
||||||
|
pub const ns_per_week = 7 * ns_per_day;
|
||||||
|
|
||||||
/// Divisions of a second
|
// Divisions of a microsecond.
|
||||||
pub const ns_per_s = 1000000000;
|
pub const us_per_ms = 1000;
|
||||||
pub const us_per_s = 1000000;
|
pub const us_per_s = 1000 * us_per_ms;
|
||||||
|
pub const us_per_min = 60 * us_per_s;
|
||||||
|
pub const us_per_hour = 60 * us_per_min;
|
||||||
|
pub const us_per_day = 24 * us_per_hour;
|
||||||
|
pub const us_per_week = 7 * us_per_day;
|
||||||
|
|
||||||
|
// Divisions of a millisecond.
|
||||||
pub const ms_per_s = 1000;
|
pub const ms_per_s = 1000;
|
||||||
pub const cs_per_s = 100;
|
pub const ms_per_min = 60 * ms_per_s;
|
||||||
|
pub const ms_per_hour = 60 * ms_per_min;
|
||||||
|
pub const ms_per_day = 24 * ms_per_hour;
|
||||||
|
pub const ms_per_week = 7 * ms_per_day;
|
||||||
|
|
||||||
/// Common time divisions
|
// Divisions of a second.
|
||||||
pub const s_per_min = 60;
|
pub const s_per_min = 60;
|
||||||
pub const s_per_hour = s_per_min * 60;
|
pub const s_per_hour = s_per_min * 60;
|
||||||
pub const s_per_day = s_per_hour * 24;
|
pub const s_per_day = s_per_hour * 24;
|
||||||
@ -173,43 +144,58 @@ pub const Timer = struct {
|
|||||||
resolution: u64,
|
resolution: u64,
|
||||||
start_time: u64,
|
start_time: u64,
|
||||||
|
|
||||||
const Error = error{TimerUnsupported};
|
pub const Error = error{TimerUnsupported};
|
||||||
|
|
||||||
///At some point we may change our minds on RAW, but for now we're
|
/// At some point we may change our minds on RAW, but for now we're
|
||||||
/// sticking with posix standard MONOTONIC. For more information, see:
|
/// sticking with posix standard MONOTONIC. For more information, see:
|
||||||
/// https://github.com/ziglang/zig/pull/933
|
/// https://github.com/ziglang/zig/pull/933
|
||||||
const monotonic_clock_id = os.CLOCK_MONOTONIC;
|
const monotonic_clock_id = os.CLOCK_MONOTONIC;
|
||||||
|
|
||||||
/// Initialize the timer structure.
|
/// Initialize the timer structure.
|
||||||
//This gives us an opportunity to grab the counter frequency in windows.
|
/// Can only fail when running in a hostile environment that intentionally injects
|
||||||
//On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000.
|
/// error values into syscalls, such as using seccomp on Linux to intercept
|
||||||
//On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
|
/// `clock_gettime`.
|
||||||
|
pub fn start() Error!Timer {
|
||||||
|
// This gives us an opportunity to grab the counter frequency in windows.
|
||||||
|
// On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000.
|
||||||
|
// On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
|
||||||
// supported, or if the timespec pointer is out of bounds, which should be
|
// supported, or if the timespec pointer is out of bounds, which should be
|
||||||
// impossible here barring cosmic rays or other such occurrences of
|
// impossible here barring cosmic rays or other such occurrences of
|
||||||
// incredibly bad luck.
|
// incredibly bad luck.
|
||||||
//On Darwin: This cannot fail, as far as I am able to tell.
|
// On Darwin: This cannot fail, as far as I am able to tell.
|
||||||
pub fn start() Error!Timer {
|
|
||||||
var self: Timer = undefined;
|
|
||||||
|
|
||||||
if (is_windows) {
|
if (is_windows) {
|
||||||
self.frequency = os.windows.QueryPerformanceFrequency();
|
const freq = os.windows.QueryPerformanceFrequency();
|
||||||
self.resolution = @divFloor(ns_per_s, self.frequency);
|
return Timer{
|
||||||
self.start_time = os.windows.QueryPerformanceCounter();
|
.frequency = freq,
|
||||||
|
.resolution = @divFloor(ns_per_s, freq),
|
||||||
|
.start_time = os.windows.QueryPerformanceCounter(),
|
||||||
|
};
|
||||||
} else if (comptime std.Target.current.isDarwin()) {
|
} else if (comptime std.Target.current.isDarwin()) {
|
||||||
os.darwin.mach_timebase_info(&self.frequency);
|
var freq: os.darwin.mach_timebase_info_data = undefined;
|
||||||
self.resolution = @divFloor(self.frequency.numer, self.frequency.denom);
|
os.darwin.mach_timebase_info(&freq);
|
||||||
self.start_time = os.darwin.mach_absolute_time();
|
|
||||||
|
return Timer{
|
||||||
|
.frequency = freq,
|
||||||
|
.resolution = @divFloor(freq.numer, freq.denom),
|
||||||
|
.start_time = os.darwin.mach_absolute_time(),
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
//On Linux, seccomp can do arbitrary things to our ability to call
|
// On Linux, seccomp can do arbitrary things to our ability to call
|
||||||
// syscalls, including return any errno value it wants and
|
// syscalls, including return any errno value it wants and
|
||||||
// inconsistently throwing errors. Since we can't account for
|
// inconsistently throwing errors. Since we can't account for
|
||||||
// abuses of seccomp in a reasonable way, we'll assume that if
|
// abuses of seccomp in a reasonable way, we'll assume that if
|
||||||
// seccomp is going to block us it will at least do so consistently
|
// seccomp is going to block us it will at least do so consistently
|
||||||
var ts: os.timespec = undefined;
|
var res: os.timespec = undefined;
|
||||||
os.clock_getres(monotonic_clock_id, &ts) catch return error.TimerUnsupported;
|
os.clock_getres(monotonic_clock_id, &res) catch return error.TimerUnsupported;
|
||||||
self.resolution = @intCast(u64, ts.tv_sec) * @as(u64, ns_per_s) + @intCast(u64, ts.tv_nsec);
|
|
||||||
|
|
||||||
|
var ts: os.timespec = undefined;
|
||||||
os.clock_gettime(monotonic_clock_id, &ts) catch return error.TimerUnsupported;
|
os.clock_gettime(monotonic_clock_id, &ts) catch return error.TimerUnsupported;
|
||||||
self.start_time = @intCast(u64, ts.tv_sec) * @as(u64, ns_per_s) + @intCast(u64, ts.tv_nsec);
|
|
||||||
|
return Timer{
|
||||||
|
.resolution = @intCast(u64, res.tv_sec) * ns_per_s + @intCast(u64, res.tv_nsec),
|
||||||
|
.start_time = @intCast(u64, ts.tv_sec) * ns_per_s + @intCast(u64, ts.tv_nsec),
|
||||||
|
.frequency = {},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -262,7 +248,6 @@ test "sleep" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "timestamp" {
|
test "timestamp" {
|
||||||
const ns_per_ms = (ns_per_s / ms_per_s);
|
|
||||||
const margin = ns_per_ms * 50;
|
const margin = ns_per_ms * 50;
|
||||||
|
|
||||||
const time_0 = milliTimestamp();
|
const time_0 = milliTimestamp();
|
||||||
@ -273,7 +258,6 @@ test "timestamp" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "Timer" {
|
test "Timer" {
|
||||||
const ns_per_ms = (ns_per_s / ms_per_s);
|
|
||||||
const margin = ns_per_ms * 150;
|
const margin = ns_per_ms * 150;
|
||||||
|
|
||||||
var timer = try Timer.start();
|
var timer = try Timer.start();
|
||||||
|
|||||||
@ -1,15 +1,26 @@
|
|||||||
/// Epoch reference times in terms of their difference from
|
//! Epoch reference times in terms of their difference from
|
||||||
/// posix epoch in seconds.
|
//! UTC 1970-01-01 in seconds.
|
||||||
pub const posix = 0; //Jan 01, 1970 AD
|
|
||||||
pub const dos = 315532800; //Jan 01, 1980 AD
|
/// Jan 01, 1970 AD
|
||||||
pub const ios = 978307200; //Jan 01, 2001 AD
|
pub const posix = 0;
|
||||||
pub const openvms = -3506716800; //Nov 17, 1858 AD
|
/// Jan 01, 1980 AD
|
||||||
pub const zos = -2208988800; //Jan 01, 1900 AD
|
pub const dos = 315532800;
|
||||||
pub const windows = -11644473600; //Jan 01, 1601 AD
|
/// Jan 01, 2001 AD
|
||||||
pub const amiga = 252460800; //Jan 01, 1978 AD
|
pub const ios = 978307200;
|
||||||
pub const pickos = -63244800; //Dec 31, 1967 AD
|
/// Nov 17, 1858 AD
|
||||||
pub const gps = 315964800; //Jan 06, 1980 AD
|
pub const openvms = -3506716800;
|
||||||
pub const clr = -62135769600; //Jan 01, 0001 AD
|
/// Jan 01, 1900 AD
|
||||||
|
pub const zos = -2208988800;
|
||||||
|
/// Jan 01, 1601 AD
|
||||||
|
pub const windows = -11644473600;
|
||||||
|
/// Jan 01, 1978 AD
|
||||||
|
pub const amiga = 252460800;
|
||||||
|
/// Dec 31, 1967 AD
|
||||||
|
pub const pickos = -63244800;
|
||||||
|
/// Jan 06, 1980 AD
|
||||||
|
pub const gps = 315964800;
|
||||||
|
/// Jan 01, 0001 AD
|
||||||
|
pub const clr = -62135769600;
|
||||||
|
|
||||||
pub const unix = posix;
|
pub const unix = posix;
|
||||||
pub const android = posix;
|
pub const android = posix;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user