commit 786bc69f83d902972b4b9dfb520679a746f1a544
parent 16079e6475f338574113eb1984ecdf3f5868e61d
Author: Mykyta Holubakha <hilobakho@gmail.com>
Date: Thu, 18 Mar 2021 17:27:22 +0200
time::now: use vdso
Diffstat:
6 files changed, 111 insertions(+), 70 deletions(-)
diff --git a/gen-stdlib b/gen-stdlib
@@ -477,9 +477,10 @@ strio() {
time() {
printf '# time\n'
gen_srcs time \
- '$(PLATFORM).ha' \
+ '$(PLATFORM)/functions.ha' \
+ '$(PLATFORM)/$(ARCH).ha' \
types.ha
- gen_ssa time
+ gen_ssa time linux::vdso
}
temp() {
diff --git a/stdlib.mk b/stdlib.mk
@@ -582,10 +582,11 @@ $(HARECACHE)/temp/temp.ssa: $(stdlib_temp_srcs) $(stdlib_rt) $(stdlib_crypto_ran
# time
stdlib_time_srcs= \
- $(STDLIB)/time/$(PLATFORM).ha \
+ $(STDLIB)/time/$(PLATFORM)/functions.ha \
+ $(STDLIB)/time/$(PLATFORM)/$(ARCH).ha \
$(STDLIB)/time/types.ha
-$(HARECACHE)/time/time.ssa: $(stdlib_time_srcs) $(stdlib_rt)
+$(HARECACHE)/time/time.ssa: $(stdlib_time_srcs) $(stdlib_rt) $(stdlib_linux_vdso)
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/time
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ntime \
@@ -1198,10 +1199,11 @@ $(TESTCACHE)/temp/temp.ssa: $(testlib_temp_srcs) $(testlib_rt) $(testlib_crypto_
# time
testlib_time_srcs= \
- $(STDLIB)/time/$(PLATFORM).ha \
+ $(STDLIB)/time/$(PLATFORM)/functions.ha \
+ $(STDLIB)/time/$(PLATFORM)/$(ARCH).ha \
$(STDLIB)/time/types.ha
-$(TESTCACHE)/time/time.ssa: $(testlib_time_srcs) $(testlib_rt)
+$(TESTCACHE)/time/time.ssa: $(testlib_time_srcs) $(testlib_rt) $(testlib_linux_vdso)
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/time
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ntime \
diff --git a/time/+linux.ha b/time/+linux.ha
@@ -1,64 +0,0 @@
-use rt;
-
-fn duration_to_timespec(n: duration, ts: *rt::timespec) void = {
- ts.tv_sec = n / SECOND;
- ts.tv_nsec = n % SECOND;
-};
-
-fn time_to_timespec(t: time, ts: *rt::timespec) void = {
- ts.tv_sec = t.sec;
- ts.tv_nsec = t.nsec;
-};
-
-fn timespec_to_time(ts: rt::timespec) time = time {
- sec = ts.tv_sec,
- nsec = ts.tv_nsec,
-};
-
-// Yields the process to the kernel and returns after the requested duration.
-export fn sleep(n: duration) void = {
- let in = rt::timespec { ... };
- duration_to_timespec(n, &in);
- let req = ∈
-
- for (true) {
- let res = rt::timespec { ... };
- match (rt::nanosleep(req, &res)) {
- void => return,
- err: rt::errno => switch (err) {
- rt::EINTR => {
- req = &res;
- },
- * => abort("Unexpected error from nanosleep"),
- },
- };
- };
-};
-
-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,
-};
-
-// Returns the current time for a given clock.
-export fn now(clock: clock) time = {
- let tp = rt::timespec { ... };
- return match (rt::clock_gettime(clock, &tp)) {
- void => timespec_to_time(tp),
- err: rt::errno => abort("Unexpected error from clock_gettime"),
- };
-};
diff --git a/time/+linux/+aarch64.ha b/time/+linux/+aarch64.ha
@@ -0,0 +1,2 @@
+export def VDSO_CGT_SYM: str = "__kernel_clock_gettime";
+export def VDSO_CGT_VER: str = "LINUX_2.6.39";
diff --git a/time/+linux/+x86_64.ha b/time/+linux/+x86_64.ha
@@ -0,0 +1,2 @@
+export def VDSO_CGT_SYM: str = "__vdso_clock_gettime";
+export def VDSO_CGT_VER: str = "LINUX_2.6";
diff --git a/time/+linux/functions.ha b/time/+linux/functions.ha
@@ -0,0 +1,98 @@
+use rt;
+use linux::vdso;
+
+
+fn duration_to_timespec(n: duration, ts: *rt::timespec) void = {
+ ts.tv_sec = n / SECOND;
+ ts.tv_nsec = n % SECOND;
+};
+
+fn time_to_timespec(t: time, ts: *rt::timespec) void = {
+ ts.tv_sec = t.sec;
+ ts.tv_nsec = t.nsec;
+};
+
+fn timespec_to_time(ts: rt::timespec) time = time {
+ sec = ts.tv_sec,
+ nsec = ts.tv_nsec,
+};
+
+// Yields the process to the kernel and returns after the requested duration.
+export fn sleep(n: duration) void = {
+ let in = rt::timespec { ... };
+ duration_to_timespec(n, &in);
+ let req = ∈
+
+ for (true) {
+ let res = rt::timespec { ... };
+ match (rt::nanosleep(req, &res)) {
+ void => return,
+ err: rt::errno => switch (err) {
+ rt::EINTR => {
+ req = &res;
+ },
+ * => abort("Unexpected error from nanosleep"),
+ },
+ };
+ };
+};
+
+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,
+};
+
+let cgt_vdso: nullable *fn(int, *rt::timespec) int = null;
+
+fn get_cgt_vdso() nullable *fn(int, *rt::timespec) int = {
+ static let vdso_checked: bool = false;
+ if (vdso_checked)
+ return cgt_vdso;
+ vdso_checked = true;
+
+ cgt_vdso = vdso::get_vdso_sym(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) = {
+ let vfn = match (get_cgt_vdso()) {
+ null => return rt::wrap_errno(rt::ENOSYS),
+ vfn: *fn(int, *rt::timespec) int => vfn,
+ };
+ let ret = vfn(clock, tp);
+ if (ret == 0) {
+ return;
+ };
+ return rt::wrap_errno(ret);
+};
+
+// Returns the current time for a given clock.
+export fn now(clock: clock) time = {
+ let tp = rt::timespec { ... };
+ let err = match (now_vdso(clock, &tp)) {
+ void => return timespec_to_time(tp),
+ err: rt::errno => err
+ };
+ if (err != rt::wrap_errno(rt::ENOSYS)) {
+ abort("Unexpected error from clock_gettime");
+ };
+ return match (rt::clock_gettime(clock, &tp)) {
+ void => timespec_to_time(tp),
+ err: rt::errno => abort("Unexpected error from clock_gettime"),
+ };
+};