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