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