File: functions.ha

package info (click to toggle)
hare 0.25.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,948 kB
  • sloc: asm: 1,264; makefile: 123; sh: 114; lisp: 101
file content (145 lines) | stat: -rw-r--r-- 4,108 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>

use errors;
use linux::vdso;
use rt;

// Converts a [[duration]] to an [[rt::timespec]]. This function is
// non-portable.
export fn duration_to_timespec(d: duration) rt::timespec = rt::timespec {
	tv_sec = d / SECOND,
	tv_nsec = d % SECOND,
};

// Converts a [[duration]] to an [[rt::timeval]]. This function is
// non-portable.
export fn duration_to_timeval(d: duration) rt::timeval = rt::timeval {
	tv_sec = d / SECOND,
	tv_usec = d % SECOND / 1000,
};

// Converts an [[instant]] to an [[rt::timespec]]. This function is
// non-portable.
export fn instant_to_timespec(t: instant) rt::timespec = rt::timespec {
	tv_sec = t.sec,
	tv_nsec = t.nsec,
};

// Converts a [[rt::timespec]] to an [[instant]]. This function is non-portable.
export fn timespec_to_instant(ts: rt::timespec) instant = instant {
	sec = ts.tv_sec,
	nsec = ts.tv_nsec,
};

// Yields the process to the kernel and returns after the requested duration.
export fn sleep(d: duration) void = {
	let req = duration_to_timespec(d);

	for (true) {
		let res = rt::timespec { ... };
		match (rt::nanosleep(&req, &res)) {
		case void =>
			return;
		case let err: rt::errno =>
			switch (err) {
			case rt::EINTR =>
				req = res;
			case =>
				abort("Unexpected error from nanosleep");
			};
		};
	};
};

// An enumeration of clocks available on this system. Different clocks represent
// times from different epochs, and have different characteristics with regards
// to leap seconds, NTP adjustments, and so on. All systems provide the REALTIME
// and MONOTONIC clocks at least; use of other clocks is not guaranteed to be
// portable.
export type clock = enum {
	// The current wall-clock time. This may jump forwards or backwards in
	// time to account for leap seconds, NTP adjustments, etc.
	REALTIME = 0,

	// The current monotonic time. This clock measures from some undefined
	// epoch and is not affected by leap seconds, NTP adjustments, and
	// changes to the system time: it always increases by one second per
	// second.
	MONOTONIC = 1,

	// Measures CPU time consumed by the calling process.
	PROCESS_CPU = 2,

	// Time since the system was booted. Increases monotonically and, unlike
	// [[MONOTONIC]], continues to tick while the system is suspended.
	BOOT = 7,

	// This clock is like [[REALTIME]], but will wake the system if it is suspended.
	REALTIME_ALARM = 8,
	// This clock is like [[BOOT]], but will wake the system if it is suspended.
	BOOT_ALARM = 9,

	// A system-wide clock derived from wall-clock time but ignoring leap seconds.
	TAI = 11,
};

fn cgt_vdso() nullable *fn(int, *rt::timespec) int = {
	static let vdso_checked: bool = false;
	static let cgt_vdso: nullable *fn(int, *rt::timespec) int = null;
	if (vdso_checked) {
		return cgt_vdso;
	};
	vdso_checked = true;
	cgt_vdso = vdso::getsym(VDSO_CGT_SYM, VDSO_CGT_VER):
		nullable *fn(int, *rt::timespec) int;
	return cgt_vdso;
};

fn now_vdso(clock: clock, tp: *rt::timespec) (void | rt::errno) = {
	const vfn = match (cgt_vdso()) {
	case null =>
		return rt::ENOSYS;
	case let vfn: *fn(int, *rt::timespec) int =>
		yield vfn;
	};
	const ret = vfn(clock, tp);
	if (ret == 0) {
		return;
	};
	return ret;
};

// Returns the current time for a given clock.
export fn now(clock: clock) instant = {
	let tp = rt::timespec { ... };
	let err = match (now_vdso(clock, &tp)) {
	case void =>
		return timespec_to_instant(tp);
	case let err: rt::errno =>
		yield err;
	};
	if (err != rt::ENOSYS) {
		abort("Unexpected error from clock_gettime");
	};
	match (rt::clock_gettime(clock, &tp)) {
	case void =>
		return timespec_to_instant(tp);
	case let err: rt::errno =>
		abort("Unexpected error from clock_gettime");
	};
};

// Sets system clock to given time.
export fn set(clock: clock, t: instant) (void | errors::noaccess) = {
	let tp = instant_to_timespec(t);
	let err = match (rt::clock_settime(clock, &tp)) {
	case void => return;
	case let err: rt::errno =>
		yield err;
	};
	if (err == rt::EPERM) {
		return errors::noaccess;
	};
	abort("Unexpected error from clock_settime");
};