hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };