diff --git a/lib/std/c.zig b/lib/std/c.zig index cc5483cce9f4..5aaaa0309a95 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -1527,6 +1527,18 @@ pub usingnamespace switch (native_os) { }, }; +// clock_nanosleep() and TIMER_ABSTIME exist in the libc of only some Unix-like +// systems, but they are consistent everywhere they exist. +pub usingnamespace switch (native_os) { + .linux, .freebsd, .netbsd, .dragonfly => struct { + pub const TIMER = struct { + pub const ABSTIME = 0x01; + }; + pub extern "c" fn clock_nanosleep(clk_id: c_int, flags: c_int, rqtp: *const c.timespec, rmtp: ?*c.timespec) c_int; + }, + else => struct {}, +}; + pub const fstat = switch (native_os) { .macos => switch (native_arch) { .x86_64 => private.@"fstat$INODE64", diff --git a/lib/std/os.zig b/lib/std/os.zig index 541d70e3fc51..32fa52f447f8 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -135,6 +135,7 @@ pub const Stat = system.Stat; pub const T = system.T; pub const TCSA = system.TCSA; pub const TCP = system.TCP; +pub const TIMER = system.TIMER; pub const VDSO = system.VDSO; pub const W = system.W; pub const addrinfo = system.addrinfo; @@ -5812,6 +5813,48 @@ pub fn nanosleep(seconds: u64, nanoseconds: u64) void { } } +pub const ClockNanosleepError = error{ + InvalidArgument, + NotImplemented, + UnsupportedClock, +} || UnexpectedError; + +// The interface choices here are a blend of the existing ones from nanosleep() +// and clock_gettime(), and EINTR is retried internally to mirror nanosleep() +// while handling TIMER.ABSTIME properly. +pub fn clock_nanosleep(clk_id: i32, flags: i32, seconds: u64, nanoseconds: u64) ClockNanosleepError!void { + // We could also potentially do a poor emulation via clock_gettime() + + // nanosleep() on systems that have those, but for now: + if (!@hasDecl(system, "clock_nanosleep")) + @compileError("Unsupported OS"); + + var req = timespec{ + .tv_sec = std.math.cast(isize, seconds) orelse std.math.maxInt(isize), + .tv_nsec = std.math.cast(isize, nanoseconds) orelse std.math.maxInt(isize), + }; + var rem: timespec = undefined; + while (true) { + // All the POSIX/libc versions of clock_nanosleep return *positive* + // errno numbers, which are unusual. The direct Linux syscall uses + // normal -errno stuff. + const rc = system.clock_nanosleep(clk_id, flags, &req, &rem); + const errval: E = if (use_libc) @enumFromInt(rc) else errno(rc); + switch (errval) { + .SUCCESS => return, + .INTR => { + if (flags != system.TIMER.ABSTIME) + req = rem; + continue; + }, + .OPNOTSUPP => return error.UnsupportedClock, + .INVAL => return error.InvalidArgument, + .NOSYS => return error.NotImplemented, // NetBSD + .FAULT => unreachable, + else => |err| return unexpectedErrno(err), + } + } +} + pub fn dl_iterate_phdr( context: anytype, comptime Error: type, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 16a4332a4a4d..2ca5021f1fe3 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1369,6 +1369,10 @@ pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { return syscall2(.clock_settime, @as(usize, @bitCast(@as(isize, clk_id))), @intFromPtr(tp)); } +pub fn clock_nanosleep(clk_id: i32, flags: i32, rqtp: *const timespec, rmtp: ?*timespec) usize { + return std.os.linux.syscall4(.clock_nanosleep, @as(usize, @bitCast(@as(isize, clk_id))), @as(usize, @bitCast(@as(isize, flags))), @intFromPtr(rqtp), @intFromPtr(rmtp)); +} + pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) usize { return syscall2(.gettimeofday, @intFromPtr(tv), @intFromPtr(tz)); } @@ -3420,6 +3424,10 @@ pub const CLOCK = struct { pub const TAI = 11; }; +pub const TIMER = struct { + pub const ABSTIME = 0x01; +}; + pub const CSIGNAL = 0x000000ff; pub const CLONE = struct { diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index a006e6f1bce8..83a686dcb2a4 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -1284,3 +1284,10 @@ test "fchmodat smoke test" { try expectMode(tmp.dir.fd, "symlink", 0o600); try expectMode(tmp.dir.fd, "regfile", 0o640); } + +test "clock_nanosleep smoke test" { + if (!@hasDecl(os.system, "clock_nanosleep")) return error.SkipZigTest; + // Absolute sleep until CLOCK.REALTIME is >= 0,0. Should always be <= now, + // and therefore should immediately return without error. + try os.clock_nanosleep(os.CLOCK.REALTIME, os.TIMER.ABSTIME, 0, 0); +}