| author | |
| committer | |
| log | 0dcadc61b4ce8fb3ffb75b8f266bff2c6ae149b6 |
| tree | 5dcf2b82a30084f092f30033f0e23a0ca9160def |
| parent | 98b88bb52f6385cb5dbbd6bc2238939d8f45e47e |
| parent | ca4053ba49d8bd171e592783c6024795c4794fda |
12 files changed, 403 insertions(+), 54 deletions(-)
CMakeLists.txt+2| ... | @@ -510,6 +510,8 @@ set(ZIG_STD_FILES | ... | @@ -510,6 +510,8 @@ set(ZIG_STD_FILES |
| 510 | "os/linux/index.zig" | 510 | "os/linux/index.zig" |
| 511 | "os/linux/x86_64.zig" | 511 | "os/linux/x86_64.zig" |
| 512 | "os/path.zig" | 512 | "os/path.zig" |
| 513 | "os/time.zig" | ||
| 514 | "os/epoch.zig" | ||
| 513 | "os/windows/error.zig" | 515 | "os/windows/error.zig" |
| 514 | "os/windows/index.zig" | 516 | "os/windows/index.zig" |
| 515 | "os/windows/util.zig" | 517 | "os/windows/util.zig" |
std/c/darwin.zig+18| ... | @@ -3,10 +3,28 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int; | ... | @@ -3,10 +3,28 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int; |
| 3 | 3 | ||
| 4 | pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize; | 4 | pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize; |
| 5 | 5 | ||
| 6 | pub extern "c" fn mach_absolute_time() u64; | ||
| 7 | pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void; | ||
| 8 | |||
| 6 | pub use @import("../os/darwin_errno.zig"); | 9 | pub use @import("../os/darwin_errno.zig"); |
| 7 | 10 | ||
| 8 | pub const _errno = __error; | 11 | pub const _errno = __error; |
| 9 | 12 | ||
| 13 | pub const timeval = extern struct { | ||
| 14 | tv_sec: isize, | ||
| 15 | tv_usec: isize, | ||
| 16 | }; | ||
| 17 | |||
| 18 | pub const timezone = extern struct { | ||
| 19 | tz_minuteswest: i32, | ||
| 20 | tz_dsttime: i32, | ||
| 21 | }; | ||
| 22 | |||
| 23 | pub const mach_timebase_info_data = struct { | ||
| 24 | numer: u32, | ||
| 25 | denom: u32, | ||
| 26 | }; | ||
| 27 | |||
| 10 | /// Renamed to Stat to not conflict with the stat function. | 28 | /// Renamed to Stat to not conflict with the stat function. |
| 11 | pub const Stat = extern struct { | 29 | pub const Stat = extern struct { |
| 12 | dev: i32, | 30 | dev: i32, |
std/c/index.zig+1| ... | @@ -41,6 +41,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; | ... | @@ -41,6 +41,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; |
| 41 | pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize; | 41 | pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize; |
| 42 | pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8; | 42 | pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8; |
| 43 | pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int; | 43 | pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int; |
| 44 | pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int; | ||
| 44 | pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int; | 45 | pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int; |
| 45 | pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int; | 46 | pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int; |
| 46 | pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; | 47 | pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; |
std/fmt/index.zig+2-1| ... | @@ -86,7 +86,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), | ... | @@ -86,7 +86,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 86 | }, | 86 | }, |
| 87 | 's' => { | 87 | 's' => { |
| 88 | state = State.Buf; | 88 | state = State.Buf; |
| 89 | },'.' => { | 89 | }, |
| 90 | '.' => { | ||
| 90 | state = State.Float; | 91 | state = State.Float; |
| 91 | }, | 92 | }, |
| 92 | else => @compileError("Unknown format character: " ++ []u8{c}), | 93 | else => @compileError("Unknown format character: " ++ []u8{c}), |
std/os/darwin.zig+12| ... | @@ -260,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u | ... | @@ -260,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u |
| 260 | return errnoWrap(c.readlink(path, buf_ptr, buf_len)); | 260 | return errnoWrap(c.readlink(path, buf_ptr, buf_len)); |
| 261 | } | 261 | } |
| 262 | 262 | ||
| 263 | pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize { | ||
| 264 | return errnoWrap(c.gettimeofday(tv, tz)); | ||
| 265 | } | ||
| 266 | |||
| 263 | pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize { | 267 | pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize { |
| 264 | return errnoWrap(c.nanosleep(req, rem)); | 268 | return errnoWrap(c.nanosleep(req, rem)); |
| 265 | } | 269 | } |
| ... | @@ -330,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void { | ... | @@ -330,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void { |
| 330 | fn errnoWrap(value: isize) usize { | 334 | fn errnoWrap(value: isize) usize { |
| 331 | return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value); | 335 | return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value); |
| 332 | } | 336 | } |
| 337 | |||
| 338 | |||
| 339 | pub const timezone = c.timezone; | ||
| 340 | pub const timeval = c.timeval; | ||
| 341 | pub const mach_timebase_info_data = c.mach_timebase_info_data; | ||
| 342 | |||
| 343 | pub const mach_absolute_time = c.mach_absolute_time; | ||
| 344 | pub const mach_timebase_info = c.mach_timebase_info; | ||
| \ No newline at end of file | |||
std/os/epoch.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 1 | /// Epoch reference times in terms of their difference from | ||
| 2 | /// posix epoch in seconds. | ||
| 3 | pub const posix = 0; //Jan 01, 1970 AD | ||
| 4 | pub const dos = 315532800; //Jan 01, 1980 AD | ||
| 5 | pub const ios = 978307200; //Jan 01, 2001 AD | ||
| 6 | pub const openvms = -3506716800; //Nov 17, 1858 AD | ||
| 7 | pub const zos = -2208988800; //Jan 01, 1900 AD | ||
| 8 | pub const windows = -11644473600; //Jan 01, 1601 AD | ||
| 9 | pub const amiga = 252460800; //Jan 01, 1978 AD | ||
| 10 | pub const pickos = -63244800; //Dec 31, 1967 AD | ||
| 11 | pub const gps = 315964800; //Jan 06, 1980 AD | ||
| 12 | pub const clr = -62135769600; //Jan 01, 0001 AD | ||
| 13 | |||
| 14 | pub const unix = posix; | ||
| 15 | pub const android = posix; | ||
| 16 | pub const os2 = dos; | ||
| 17 | pub const bios = dos; | ||
| 18 | pub const vfat = dos; | ||
| 19 | pub const ntfs = windows; | ||
| 20 | pub const ntp = zos; | ||
| 21 | pub const jbase = pickos; | ||
| 22 | pub const aros = amiga; | ||
| 23 | pub const morphos = amiga; | ||
| 24 | pub const brew = gps; | ||
| 25 | pub const atsc = gps; | ||
| 26 | pub const go = clr; | ||
| \ No newline at end of file | |||
std/os/index.zig+4-47| ... | @@ -4,16 +4,16 @@ const Os = builtin.Os; | ... | @@ -4,16 +4,16 @@ const Os = builtin.Os; |
| 4 | const is_windows = builtin.os == Os.windows; | 4 | const is_windows = builtin.os == Os.windows; |
| 5 | const os = this; | 5 | const os = this; |
| 6 | 6 | ||
| 7 | test "std.os" { | 7 | comptime { |
| 8 | if (!builtin.is_test) return; | ||
| 8 | _ = @import("child_process.zig"); | 9 | _ = @import("child_process.zig"); |
| 9 | _ = @import("darwin.zig"); | 10 | _ = @import("darwin.zig"); |
| 10 | _ = @import("darwin_errno.zig"); | 11 | _ = @import("darwin_errno.zig"); |
| 11 | _ = @import("get_user_id.zig"); | 12 | _ = @import("get_user_id.zig"); |
| 12 | _ = @import("linux/errno.zig"); | ||
| 13 | _ = @import("linux/index.zig"); | 13 | _ = @import("linux/index.zig"); |
| 14 | _ = @import("linux/x86_64.zig"); | ||
| 15 | _ = @import("path.zig"); | 14 | _ = @import("path.zig"); |
| 16 | _ = @import("test.zig"); | 15 | _ = @import("test.zig"); |
| 16 | _ = @import("time.zig"); | ||
| 17 | _ = @import("windows/index.zig"); | 17 | _ = @import("windows/index.zig"); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| ... | @@ -32,6 +32,7 @@ pub const net = @import("net.zig"); | ... | @@ -32,6 +32,7 @@ pub const net = @import("net.zig"); |
| 32 | pub const ChildProcess = @import("child_process.zig").ChildProcess; | 32 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| 33 | pub const path = @import("path.zig"); | 33 | pub const path = @import("path.zig"); |
| 34 | pub const File = @import("file.zig").File; | 34 | pub const File = @import("file.zig").File; |
| 35 | pub const time = @import("time.zig"); | ||
| 35 | 36 | ||
| 36 | pub const FileMode = switch (builtin.os) { | 37 | pub const FileMode = switch (builtin.os) { |
| 37 | Os.windows => void, | 38 | Os.windows => void, |
| ... | @@ -1379,50 +1380,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { | ... | @@ -1379,50 +1380,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { |
| 1379 | } | 1380 | } |
| 1380 | } | 1381 | } |
| 1381 | 1382 | ||
| 1382 | pub fn sleep(seconds: usize, nanoseconds: usize) void { | ||
| 1383 | switch(builtin.os) { | ||
| 1384 | Os.linux, Os.macosx, Os.ios => { | ||
| 1385 | posixSleep(u63(seconds), u63(nanoseconds)); | ||
| 1386 | }, | ||
| 1387 | Os.windows => { | ||
| 1388 | const milliseconds = seconds * 1000 + nanoseconds / 1000000; | ||
| 1389 | windows.Sleep(windows.DWORD(milliseconds)); | ||
| 1390 | }, | ||
| 1391 | else => @compileError("Unsupported OS"), | ||
| 1392 | } | ||
| 1393 | } | ||
| 1394 | |||
| 1395 | const u63 = @IntType(false, 63); | ||
| 1396 | pub fn posixSleep(seconds: u63, nanoseconds: u63) void { | ||
| 1397 | var req = posix.timespec { | ||
| 1398 | .tv_sec = seconds, | ||
| 1399 | .tv_nsec = nanoseconds, | ||
| 1400 | }; | ||
| 1401 | var rem: posix.timespec = undefined; | ||
| 1402 | while (true) { | ||
| 1403 | const ret_val = posix.nanosleep(&req, &rem); | ||
| 1404 | const err = posix.getErrno(ret_val); | ||
| 1405 | if (err == 0) return; | ||
| 1406 | switch (err) { | ||
| 1407 | posix.EFAULT => unreachable, | ||
| 1408 | posix.EINVAL => { | ||
| 1409 | // Sometimes Darwin returns EINVAL for no reason. | ||
| 1410 | // We treat it as a spurious wakeup. | ||
| 1411 | return; | ||
| 1412 | }, | ||
| 1413 | posix.EINTR => { | ||
| 1414 | req = rem; | ||
| 1415 | continue; | ||
| 1416 | }, | ||
| 1417 | else => return, | ||
| 1418 | } | ||
| 1419 | } | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | test "os.sleep" { | ||
| 1423 | sleep(0, 1); | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | pub fn posix_setuid(uid: u32) !void { | 1383 | pub fn posix_setuid(uid: u32) !void { |
| 1427 | const err = posix.getErrno(posix.setuid(uid)); | 1384 | const err = posix.getErrno(posix.setuid(uid)); |
| 1428 | if (err == 0) return; | 1385 | if (err == 0) return; |
std/os/linux/index.zig+23-6| ... | @@ -805,6 +805,26 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize { | ... | @@ -805,6 +805,26 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize { |
| 805 | return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); | 805 | return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); |
| 806 | } | 806 | } |
| 807 | 807 | ||
| 808 | pub fn clock_gettime(clk_id: i32, tp: &timespec) usize { | ||
| 809 | return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 810 | } | ||
| 811 | |||
| 812 | pub fn clock_getres(clk_id: i32, tp: &timespec) usize { | ||
| 813 | return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 814 | } | ||
| 815 | |||
| 816 | pub fn clock_settime(clk_id: i32, tp: &const timespec) usize { | ||
| 817 | return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 818 | } | ||
| 819 | |||
| 820 | pub fn gettimeofday(tv: &timeval, tz: &timezone) usize { | ||
| 821 | return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); | ||
| 822 | } | ||
| 823 | |||
| 824 | pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize { | ||
| 825 | return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); | ||
| 826 | } | ||
| 827 | |||
| 808 | pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize { | 828 | pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize { |
| 809 | return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); | 829 | return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); |
| 810 | } | 830 | } |
| ... | @@ -1289,10 +1309,7 @@ pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize { | ... | @@ -1289,10 +1309,7 @@ pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize { |
| 1289 | return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); | 1309 | return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); |
| 1290 | } | 1310 | } |
| 1291 | 1311 | ||
| 1292 | test "import linux test" { | 1312 | comptime { |
| 1293 | // TODO lazy analysis should prevent this test from being compiled on windows, but | 1313 | if (!builtin.is_test) return; |
| 1294 | // it is still compiled on windows | 1314 | _ = @import("test.zig"); |
| 1295 | if (builtin.os == builtin.Os.linux) { | ||
| 1296 | _ = @import("test.zig"); | ||
| 1297 | } | ||
| 1298 | } | 1315 | } |
std/os/linux/test.zig+3| ... | @@ -1,8 +1,11 @@ | ... | @@ -1,8 +1,11 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../index.zig"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const linux = std.os.linux; | 3 | const linux = std.os.linux; |
| 3 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 4 | 5 | ||
| 5 | test "timer" { | 6 | test "timer" { |
| 7 | if (builtin.os != builtin.Os.linux) return; | ||
| 8 | |||
| 6 | const epoll_fd = linux.epoll_create(); | 9 | const epoll_fd = linux.epoll_create(); |
| 7 | var err = linux.getErrno(epoll_fd); | 10 | var err = linux.getErrno(epoll_fd); |
| 8 | assert(err == 0); | 11 | assert(err == 0); |
std/os/linux/x86_64.zig+10| ... | @@ -492,6 +492,16 @@ pub const timespec = extern struct { | ... | @@ -492,6 +492,16 @@ pub const timespec = extern struct { |
| 492 | tv_nsec: isize, | 492 | tv_nsec: isize, |
| 493 | }; | 493 | }; |
| 494 | 494 | ||
| 495 | pub const timeval = extern struct { | ||
| 496 | tv_sec: isize, | ||
| 497 | tv_usec: isize, | ||
| 498 | }; | ||
| 499 | |||
| 500 | pub const timezone = extern struct { | ||
| 501 | tz_minuteswest: i32, | ||
| 502 | tz_dsttime: i32, | ||
| 503 | }; | ||
| 504 | |||
| 495 | pub const dirent = extern struct { | 505 | pub const dirent = extern struct { |
| 496 | d_ino: usize, | 506 | d_ino: usize, |
| 497 | d_off: usize, | 507 | d_off: usize, |
std/os/time.zig created+288| ... | @@ -0,0 +1,288 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Os = builtin.Os; | ||
| 4 | const debug = std.debug; | ||
| 5 | |||
| 6 | const windows = std.os.windows; | ||
| 7 | const linux = std.os.linux; | ||
| 8 | const darwin = std.os.darwin; | ||
| 9 | const posix = std.os.posix; | ||
| 10 | |||
| 11 | pub const epoch = @import("epoch.zig"); | ||
| 12 | |||
| 13 | /// Sleep for the specified duration | ||
| 14 | pub fn sleep(seconds: usize, nanoseconds: usize) void { | ||
| 15 | switch (builtin.os) { | ||
| 16 | Os.linux, Os.macosx, Os.ios => { | ||
| 17 | posixSleep(u63(seconds), u63(nanoseconds)); | ||
| 18 | }, | ||
| 19 | Os.windows => { | ||
| 20 | const ns_per_ms = ns_per_s / ms_per_s; | ||
| 21 | const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms; | ||
| 22 | windows.Sleep(windows.DWORD(milliseconds)); | ||
| 23 | }, | ||
| 24 | else => @compileError("Unsupported OS"), | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | const u63 = @IntType(false, 63); | ||
| 29 | pub fn posixSleep(seconds: u63, nanoseconds: u63) void { | ||
| 30 | var req = posix.timespec { | ||
| 31 | .tv_sec = seconds, | ||
| 32 | .tv_nsec = nanoseconds, | ||
| 33 | }; | ||
| 34 | var rem: posix.timespec = undefined; | ||
| 35 | while (true) { | ||
| 36 | const ret_val = posix.nanosleep(&req, &rem); | ||
| 37 | const err = posix.getErrno(ret_val); | ||
| 38 | if (err == 0) return; | ||
| 39 | switch (err) { | ||
| 40 | posix.EFAULT => unreachable, | ||
| 41 | posix.EINVAL => { | ||
| 42 | // Sometimes Darwin returns EINVAL for no reason. | ||
| 43 | // We treat it as a spurious wakeup. | ||
| 44 | return; | ||
| 45 | }, | ||
| 46 | posix.EINTR => { | ||
| 47 | req = rem; | ||
| 48 | continue; | ||
| 49 | }, | ||
| 50 | else => return, | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | /// Get the posix timestamp, UTC, in seconds | ||
| 56 | pub fn timestamp() u64 { | ||
| 57 | return @divFloor(milliTimestamp(), ms_per_s); | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Get the posix timestamp, UTC, in milliseconds | ||
| 61 | pub const milliTimestamp = switch (builtin.os) { | ||
| 62 | Os.windows => milliTimestampWindows, | ||
| 63 | Os.linux => milliTimestampPosix, | ||
| 64 | Os.macosx, Os.ios => milliTimestampDarwin, | ||
| 65 | else => @compileError("Unsupported OS"), | ||
| 66 | }; | ||
| 67 | |||
| 68 | fn milliTimestampWindows() u64 { | ||
| 69 | //FileTime has a granularity of 100 nanoseconds | ||
| 70 | // and uses the NTFS/Windows epoch | ||
| 71 | var ft: i64 = undefined; | ||
| 72 | windows.GetSystemTimeAsFileTime(&ft); | ||
| 73 | const hns_per_ms = (ns_per_s / 100) / ms_per_s; | ||
| 74 | const epoch_adj = epoch.windows * ms_per_s; | ||
| 75 | return u64(@divFloor(ft, hns_per_ms) + epoch_adj); | ||
| 76 | } | ||
| 77 | |||
| 78 | fn milliTimestampDarwin() u64 { | ||
| 79 | //Sources suggest MacOS 10.12 has support for | ||
| 80 | // posix clock_gettime. | ||
| 81 | var tv: darwin.timeval = undefined; | ||
| 82 | var err = darwin.gettimeofday(&tv, null); | ||
| 83 | debug.assert(err == 0); | ||
| 84 | const sec_ms = u64(tv.tv_sec) * ms_per_s; | ||
| 85 | const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s); | ||
| 86 | return u64(sec_ms) + u64(usec_ms); | ||
| 87 | } | ||
| 88 | |||
| 89 | fn milliTimestampPosix() u64 { | ||
| 90 | //From what I can tell there's no reason clock_gettime | ||
| 91 | // should ever fail for us with CLOCK_REALTIME, | ||
| 92 | // seccomp aside. | ||
| 93 | var ts: posix.timespec = undefined; | ||
| 94 | const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts); | ||
| 95 | debug.assert(err == 0); | ||
| 96 | const sec_ms = u64(ts.tv_sec) * ms_per_s; | ||
| 97 | const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s); | ||
| 98 | return sec_ms + nsec_ms; | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Divisions of a second | ||
| 102 | pub const ns_per_s = 1000000000; | ||
| 103 | pub const us_per_s = 1000000; | ||
| 104 | pub const ms_per_s = 1000; | ||
| 105 | pub const cs_per_s = 100; | ||
| 106 | |||
| 107 | /// Common time divisions | ||
| 108 | pub const s_per_min = 60; | ||
| 109 | pub const s_per_hour = s_per_min * 60; | ||
| 110 | pub const s_per_day = s_per_hour * 24; | ||
| 111 | pub const s_per_week = s_per_day * 7; | ||
| 112 | |||
| 113 | |||
| 114 | /// A monotonic high-performance timer. | ||
| 115 | /// Timer.start() must be called to initialize the struct, which captures | ||
| 116 | /// the counter frequency on windows and darwin, records the resolution, | ||
| 117 | /// and gives the user an oportunity to check for the existnece of | ||
| 118 | /// monotonic clocks without forcing them to check for error on each read. | ||
| 119 | /// .resolution is in nanoseconds on all platforms but .start_time's meaning | ||
| 120 | /// depends on the OS. On Windows and Darwin it is a hardware counter | ||
| 121 | /// value that requires calculation to convert to a meaninful unit. | ||
| 122 | pub const Timer = struct { | ||
| 123 | |||
| 124 | //if we used resolution's value when performing the | ||
| 125 | // performance counter calc on windows/darwin, it would | ||
| 126 | // be less precise | ||
| 127 | frequency: switch (builtin.os) { | ||
| 128 | Os.windows => u64, | ||
| 129 | Os.macosx, Os.ios => darwin.mach_timebase_info_data, | ||
| 130 | else => void, | ||
| 131 | }, | ||
| 132 | resolution: u64, | ||
| 133 | start_time: u64, | ||
| 134 | |||
| 135 | |||
| 136 | //At some point we may change our minds on RAW, but for now we're | ||
| 137 | // sticking with posix standard MONOTONIC. For more information, see: | ||
| 138 | // https://github.com/zig-lang/zig/pull/933 | ||
| 139 | // | ||
| 140 | //const monotonic_clock_id = switch(builtin.os) { | ||
| 141 | // Os.linux => linux.CLOCK_MONOTONIC_RAW, | ||
| 142 | // else => posix.CLOCK_MONOTONIC, | ||
| 143 | //}; | ||
| 144 | const monotonic_clock_id = posix.CLOCK_MONOTONIC; | ||
| 145 | |||
| 146 | |||
| 147 | /// Initialize the timer structure. | ||
| 148 | //This gives us an oportunity to grab the counter frequency in windows. | ||
| 149 | //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000. | ||
| 150 | //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not | ||
| 151 | // supported, or if the timespec pointer is out of bounds, which should be | ||
| 152 | // impossible here barring cosmic rays or other such occurances of | ||
| 153 | // incredibly bad luck. | ||
| 154 | //On Darwin: This cannot fail, as far as I am able to tell. | ||
| 155 | const TimerError = error{TimerUnsupported, Unexpected}; | ||
| 156 | pub fn start() TimerError!Timer { | ||
| 157 | var self: Timer = undefined; | ||
| 158 | |||
| 159 | switch (builtin.os) { | ||
| 160 | Os.windows => { | ||
| 161 | var freq: i64 = undefined; | ||
| 162 | var err = windows.QueryPerformanceFrequency(&freq); | ||
| 163 | if (err == 0) return error.TimerUnsupported; | ||
| 164 | self.frequency = u64(freq); | ||
| 165 | self.resolution = @divFloor(ns_per_s, self.frequency); | ||
| 166 | |||
| 167 | var start_time: i64 = undefined; | ||
| 168 | err = windows.QueryPerformanceCounter(&start_time); | ||
| 169 | debug.assert(err != 0); | ||
| 170 | self.start_time = u64(start_time); | ||
| 171 | }, | ||
| 172 | Os.linux => { | ||
| 173 | //On Linux, seccomp can do arbitrary things to our ability to call | ||
| 174 | // syscalls, including return any errno value it wants and | ||
| 175 | // inconsistently throwing errors. Since we can't account for | ||
| 176 | // abuses of seccomp in a reasonable way, we'll assume that if | ||
| 177 | // seccomp is going to block us it will at least do so consistently | ||
| 178 | var ts: posix.timespec = undefined; | ||
| 179 | var result = posix.clock_getres(monotonic_clock_id, &ts); | ||
| 180 | var errno = posix.getErrno(result); | ||
| 181 | switch (errno) { | ||
| 182 | 0 => {}, | ||
| 183 | posix.EINVAL => return error.TimerUnsupported, | ||
| 184 | else => return std.os.unexpectedErrorPosix(errno), | ||
| 185 | } | ||
| 186 | self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); | ||
| 187 | |||
| 188 | result = posix.clock_gettime(monotonic_clock_id, &ts); | ||
| 189 | errno = posix.getErrno(result); | ||
| 190 | if (errno != 0) return std.os.unexpectedErrorPosix(errno); | ||
| 191 | self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); | ||
| 192 | }, | ||
| 193 | Os.macosx, Os.ios => { | ||
| 194 | darwin.mach_timebase_info(&self.frequency); | ||
| 195 | self.resolution = @divFloor(self.frequency.numer, self.frequency.denom); | ||
| 196 | self.start_time = darwin.mach_absolute_time(); | ||
| 197 | }, | ||
| 198 | else => @compileError("Unsupported OS"), | ||
| 199 | } | ||
| 200 | return self; | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Reads the timer value since start or the last reset in nanoseconds | ||
| 204 | pub fn read(self: &Timer) u64 { | ||
| 205 | var clock = clockNative() - self.start_time; | ||
| 206 | return switch (builtin.os) { | ||
| 207 | Os.windows => @divFloor(clock * ns_per_s, self.frequency), | ||
| 208 | Os.linux => clock, | ||
| 209 | Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom), | ||
| 210 | else => @compileError("Unsupported OS"), | ||
| 211 | }; | ||
| 212 | } | ||
| 213 | |||
| 214 | /// Resets the timer value to 0/now. | ||
| 215 | pub fn reset(self: &Timer) void | ||
| 216 | { | ||
| 217 | self.start_time = clockNative(); | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Returns the current value of the timer in nanoseconds, then resets it | ||
| 221 | pub fn lap(self: &Timer) u64 { | ||
| 222 | var now = clockNative(); | ||
| 223 | var lap_time = self.read(); | ||
| 224 | self.start_time = now; | ||
| 225 | return lap_time; | ||
| 226 | } | ||
| 227 | |||
| 228 | |||
| 229 | const clockNative = switch (builtin.os) { | ||
| 230 | Os.windows => clockWindows, | ||
| 231 | Os.linux => clockLinux, | ||
| 232 | Os.macosx, Os.ios => clockDarwin, | ||
| 233 | else => @compileError("Unsupported OS"), | ||
| 234 | }; | ||
| 235 | |||
| 236 | fn clockWindows() u64 { | ||
| 237 | var result: i64 = undefined; | ||
| 238 | var err = windows.QueryPerformanceCounter(&result); | ||
| 239 | debug.assert(err != 0); | ||
| 240 | return u64(result); | ||
| 241 | } | ||
| 242 | |||
| 243 | fn clockDarwin() u64 { | ||
| 244 | return darwin.mach_absolute_time(); | ||
| 245 | } | ||
| 246 | |||
| 247 | fn clockLinux() u64 { | ||
| 248 | var ts: posix.timespec = undefined; | ||
| 249 | var result = posix.clock_gettime(monotonic_clock_id, &ts); | ||
| 250 | debug.assert(posix.getErrno(result) == 0); | ||
| 251 | return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); | ||
| 252 | } | ||
| 253 | }; | ||
| 254 | |||
| 255 | |||
| 256 | |||
| 257 | |||
| 258 | |||
| 259 | test "os.time.sleep" { | ||
| 260 | sleep(0, 1); | ||
| 261 | } | ||
| 262 | |||
| 263 | test "os.time.timestamp" { | ||
| 264 | const ns_per_ms = (ns_per_s / ms_per_s); | ||
| 265 | const margin = 50; | ||
| 266 | |||
| 267 | const time_0 = milliTimestamp(); | ||
| 268 | sleep(0, ns_per_ms); | ||
| 269 | const time_1 = milliTimestamp(); | ||
| 270 | const interval = time_1 - time_0; | ||
| 271 | debug.assert(interval > 0 and interval < margin); | ||
| 272 | } | ||
| 273 | |||
| 274 | test "os.time.Timer" { | ||
| 275 | const ns_per_ms = (ns_per_s / ms_per_s); | ||
| 276 | const margin = ns_per_ms * 50; | ||
| 277 | |||
| 278 | var timer = try Timer.start(); | ||
| 279 | sleep(0, 10 * ns_per_ms); | ||
| 280 | const time_0 = timer.read(); | ||
| 281 | debug.assert(time_0 > 0 and time_0 < margin); | ||
| 282 | |||
| 283 | const time_1 = timer.lap(); | ||
| 284 | debug.assert(time_1 > time_0); | ||
| 285 | |||
| 286 | timer.reset(); | ||
| 287 | debug.assert(timer.read() < time_1); | ||
| 288 | } | ||
| \ No newline at end of file | |||
std/os/windows/index.zig+14| ... | @@ -1,3 +1,10 @@ | ... | @@ -1,3 +1,10 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 2 | comptime { | ||
| 3 | if (!builtin.is_test) return; | ||
| 4 | _ = @import("util.zig"); | ||
| 5 | } | ||
| 6 | |||
| 7 | |||
| 1 | pub const ERROR = @import("error.zig"); | 8 | pub const ERROR = @import("error.zig"); |
| 2 | 9 | ||
| 3 | pub extern "advapi32" stdcallcc fn CryptAcquireContextA(phProv: &HCRYPTPROV, pszContainer: ?LPCSTR, | 10 | pub extern "advapi32" stdcallcc fn CryptAcquireContextA(phProv: &HCRYPTPROV, pszContainer: ?LPCSTR, |
| ... | @@ -61,6 +68,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz | ... | @@ -61,6 +68,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz |
| 61 | 68 | ||
| 62 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; | 69 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; |
| 63 | 70 | ||
| 71 | pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void; | ||
| 72 | |||
| 64 | pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; | 73 | pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; |
| 65 | pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; | 74 | pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; |
| 66 | pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void; | 75 | pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void; |
| ... | @@ -77,6 +86,10 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem | ... | @@ -77,6 +86,10 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem |
| 77 | 86 | ||
| 78 | pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR, | 87 | pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR, |
| 79 | dwFlags: DWORD) BOOL; | 88 | dwFlags: DWORD) BOOL; |
| 89 | |||
| 90 | pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL; | ||
| 91 | |||
| 92 | pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL; | ||
| 80 | 93 | ||
| 81 | pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; | 94 | pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; |
| 82 | 95 | ||
| ... | @@ -139,6 +152,7 @@ pub const UNICODE = false; | ... | @@ -139,6 +152,7 @@ pub const UNICODE = false; |
| 139 | pub const WCHAR = u16; | 152 | pub const WCHAR = u16; |
| 140 | pub const WORD = u16; | 153 | pub const WORD = u16; |
| 141 | pub const LARGE_INTEGER = i64; | 154 | pub const LARGE_INTEGER = i64; |
| 155 | pub const FILETIME = i64; | ||
| 142 | 156 | ||
| 143 | pub const TRUE = 1; | 157 | pub const TRUE = 1; |
| 144 | pub const FALSE = 0; | 158 | pub const FALSE = 0; |