hare

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

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