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