hare

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

functions.ha (3724B)


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