hare

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

functions.ha (4108B)


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