functions.ha (3520B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use linux::vdso; 5 use rt; 6 7 // Converts a [[duration]] to an [[rt::timespec]]. This function is 8 // non-portable. 9 export fn duration_to_timespec(d: duration) rt::timespec = rt::timespec { 10 tv_sec = d / SECOND, 11 tv_nsec = d % SECOND, 12 }; 13 14 // Converts an [[instant]] to an [[rt::timespec]]. This function is 15 // non-portable. 16 export fn instant_to_timespec(t: instant) rt::timespec = rt::timespec { 17 tv_sec = t.sec, 18 tv_nsec = t.nsec, 19 }; 20 21 // Converts a [[rt::timespec]] to an [[instant]]. This function is non-portable. 22 export fn timespec_to_instant(ts: rt::timespec) instant = instant { 23 sec = ts.tv_sec, 24 nsec = ts.tv_nsec, 25 }; 26 27 // Yields the process to the kernel and returns after the requested duration. 28 export fn sleep(d: duration) void = { 29 let req = duration_to_timespec(d); 30 31 for (true) { 32 let res = rt::timespec { ... }; 33 match (rt::nanosleep(&req, &res)) { 34 case void => 35 return; 36 case let err: rt::errno => 37 switch (err) { 38 case rt::EINTR => 39 req = res; 40 case => 41 abort("Unexpected error from nanosleep"); 42 }; 43 }; 44 }; 45 }; 46 47 // An enumeration of clocks available on this system. Different clocks represent 48 // times from different epochs, and have different characteristics with regards 49 // to leap seconds, NTP adjustments, and so on. All systems provide the REALTIME 50 // and MONOTONIC clocks at least; use of other clocks is not guaranteed to be 51 // portable. 52 export type clock = enum { 53 // The current wall-clock time. This may jump forwards or backwards in 54 // time to account for leap seconds, NTP adjustments, etc. 55 REALTIME = 0, 56 57 // The current monotonic time. This clock measures from some undefined 58 // epoch and is not affected by leap seconds, NTP adjustments, and 59 // changes to the system time: it always increases by one second per 60 // second. 61 MONOTONIC = 1, 62 63 // Measures CPU time consumed by the calling process. 64 PROCESS_CPU = 2, 65 66 // Time since the system was booted. Increases monotonically and, unlike 67 // [[MONOTONIC]], continues to tick while the system is suspended. 68 BOOT = 7, 69 70 // This clock is like [[REALTIME]], but will wake the system if it is suspended. 71 REALTIME_ALARM = 8, 72 // This clock is like [[BOOT]], but will wake the system if it is suspended. 73 BOOT_ALARM = 9, 74 75 // A system-wide clock derived from wall-clock time but ignoring leap seconds. 76 TAI = 11, 77 }; 78 79 fn cgt_vdso() nullable *fn(int, *rt::timespec) int = { 80 static let vdso_checked: bool = false; 81 static let cgt_vdso: nullable *fn(int, *rt::timespec) int = null; 82 if (vdso_checked) { 83 return cgt_vdso; 84 }; 85 vdso_checked = true; 86 cgt_vdso = vdso::getsym(VDSO_CGT_SYM, VDSO_CGT_VER): 87 nullable *fn(int, *rt::timespec) int; 88 return cgt_vdso; 89 }; 90 91 fn now_vdso(clock: clock, tp: *rt::timespec) (void | rt::errno) = { 92 const vfn = match (cgt_vdso()) { 93 case null => 94 return rt::ENOSYS; 95 case let vfn: *fn(int, *rt::timespec) int => 96 yield vfn; 97 }; 98 const ret = vfn(clock, tp); 99 if (ret == 0) { 100 return; 101 }; 102 return ret; 103 }; 104 105 // Returns the current time for a given clock. 106 export fn now(clock: clock) instant = { 107 let tp = rt::timespec { ... }; 108 let err = match (now_vdso(clock, &tp)) { 109 case void => 110 return timespec_to_instant(tp); 111 case let err: rt::errno => 112 yield err; 113 }; 114 if (err != rt::ENOSYS) { 115 abort("Unexpected error from clock_gettime"); 116 }; 117 match (rt::clock_gettime(clock, &tp)) { 118 case void => 119 return timespec_to_instant(tp); 120 case let err: rt::errno => 121 abort("Unexpected error from clock_gettime"); 122 }; 123 };