hare

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

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 = &in;
     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 };