code wiki / (root) / nx_time.nx

nx_time.nx source

↩ module page · 98 lines · 3842 B

1// nx_time.nx -- monotonic wall-clock time + duration utilities. 2// 3// nx_clock_now_ns() -- monotonic nanoseconds via the 4// clock_gettime(CLOCK_MONOTONIC) syscall (113 5// on RV64 Linux). This is what nx_trace 6// wires to once spans get real timestamps. 7// 8// Per the decade-horizon doctrine, we do NOT trust gettimeofday() 9// or wall-clock realtime here -- those skew, leap, and re-set, and 10// any logic that mixes them with monotonic produces phantom bugs 11// at boundaries. 12// 13// For LOGICAL ordering use nx_trace's tick counter; for PROFILING 14// use nx_clock_now_ns(). Don't cross-mix in the same measurement. 15// 16// Design references: 17// Linux clock_gettime(2) -- monotonic clock contract 18// Go runtime nanotime() -- platform-portable monotonic clock 19// Java System.nanoTime -- JVM monotonic clock guarantee 20// Rust std::time::Instant -- monotonic-only Duration source 21 22// nx_safety_envelope: 23// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 24// sil_target: SIL1 25// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 26// verdict: NOT_YET_EVALUATED 27 28import "syscalls.nx" 29const NX_MAGIC_1000000000: i64 = 1000000000 30const NX_MAGIC_1000000: i64 = 1000000 31const NX_MAGIC_2500: i64 = 2500 32const NX_MAGIC_2500000: i64 = 2500000 33const NX_MAGIC_2500000000: i64 = 2500000000 34 35struct NxTimespec { 36 tv_sec: i64, 37 tv_nsec: i64, 38} 39 40const NX_TIMESPEC_BYTES: i64 = 16 41const NX_CLOCK_MONOTONIC: i64 = 1 42const NX_SYS_CLOCK_GETTIME: i64 = 113 43 44// nx_clock_now_ns -- monotonic wall-clock nanoseconds. 45// Returns 0 on syscall failure (caller should treat 0 as "unknown"). 46func nx_clock_now_ns() -> i64 { 47 let buf: *u8 = sys_mmap(NX_TIMESPEC_BYTES) 48 let ts: *NxTimespec = buf as *NxTimespec 49 ts.tv_sec = 0 50 ts.tv_nsec = 0 51 let rc: i64 = __syscall(NX_SYS_CLOCK_GETTIME, NX_CLOCK_MONOTONIC, 52 buf as i64, 0, 0, 0, 0) 53 if rc < 0 { return 0 } 54 return ts.tv_sec * NX_MAGIC_1000000000 + ts.tv_nsec 55} 56 57// --- Duration helpers ---------------------------------------------- 58 59// nx_dur_ns_diff -- safe end-start delta in ns. Returns 0 if 60// inputs look invalid (zero or end < start) so callers can't 61// silently consume nonsense durations. 62func nx_dur_ns_diff(start: i64, end: i64) -> i64 { 63 if start == 0 { return 0 } 64 if end == 0 { return 0 } 65 if end < start { return 0 } 66 return end - start 67} 68 69// nx_dur_to_us -- nanoseconds to microseconds, integer floor. 70func nx_dur_to_us(ns: i64) -> i64 { return ns / 1000 } 71 72// nx_dur_to_ms -- nanoseconds to milliseconds, integer floor. 73func nx_dur_to_ms(ns: i64) -> i64 { return ns / NX_MAGIC_1000000 } 74 75// nx_dur_to_s -- nanoseconds to seconds, integer floor. 76func nx_dur_to_s(ns: i64) -> i64 { return ns / NX_MAGIC_1000000000 } 77 78// --- self-test ------------------------------------------------------ 79 80func main() -> i64 { 81 // Wall-clock: at least non-negative, monotonic across calls. 82 let w1: i64 = nx_clock_now_ns() 83 let w2: i64 = nx_clock_now_ns() 84 if w1 < 0 { return __syscall(93, 30, 0, 0, 0, 0, 0) } 85 if w2 < w1 { return __syscall(93, 31, 0, 0, 0, 0, 0) } 86 87 // Duration helpers. 88 if nx_dur_ns_diff(0, 100) != 0 { return __syscall(93, 40, 0, 0, 0, 0, 0) } 89 if nx_dur_ns_diff(100, 0) != 0 { return __syscall(93, 41, 0, 0, 0, 0, 0) } 90 if nx_dur_ns_diff(200, 100) != 0 { return __syscall(93, 42, 0, 0, 0, 0, 0) } 91 if nx_dur_ns_diff(100, 250) != 150 { return __syscall(93, 43, 0, 0, 0, 0, 0) } 92 93 if nx_dur_to_us(NX_MAGIC_2500) != 2 { return __syscall(93, 50, 0, 0, 0, 0, 0) } 94 if nx_dur_to_ms(NX_MAGIC_2500000) != 2 { return __syscall(93, 51, 0, 0, 0, 0, 0) } 95 if nx_dur_to_s(NX_MAGIC_2500000000) != 2 { return __syscall(93, 52, 0, 0, 0, 0, 0) } 96 97 return 0 98}