From 61132d6c4068937b5e4181c08a9122270823506c Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Sat, 20 May 2023 08:41:12 +0200 Subject: [PATCH 1/5] os/time and janet_gettime now use CLOCK_MONOTONIC instead of CLOCK_REALTIM, this matches the description from the documentation of os/clock. Fixes issue #1144 --- src/core/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/util.c b/src/core/util.c index 3c50bc94..4e7f8c22 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -902,7 +902,7 @@ int janet_gettime(struct timespec *spec) { } #else int janet_gettime(struct timespec *spec) { - return clock_gettime(CLOCK_REALTIME, spec); + return clock_gettime(CLOCK_MONOTONIC, spec); } #endif #endif From aaf3d08bcd9ba75792cd99f9c866f8ab0ce9fb0c Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Sat, 20 May 2023 11:41:25 +0200 Subject: [PATCH 2/5] Added 'source' argument to os/clock to select the clock source --- src/core/os.c | 30 ++++++++++++++++++++++++------ src/core/util.c | 16 ++++++++++++++-- src/core/util.h | 7 ++++++- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/core/os.c b/src/core/os.c index 64a03470..7a932f86 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -1278,14 +1278,32 @@ JANET_CORE_FN(os_time, } JANET_CORE_FN(os_clock, - "(os/clock)", - "Return the number of whole + fractional seconds since some fixed point in time. The clock " - "is guaranteed to be non-decreasing in real time.") { + "(os/clock &opt source)", + "Return the number of whole + fractional seconds of the requested clock source.\n\n" + "The `source` argument is the identifier of the particular clock source to use, when not " + "specified the default is `:realtime`:\n" + "- :realtime: Return the real (i.e., wall-clock) time. This clock is affected by discontinuous " + " jumps in the system time\n" + "- :monotonic: Return the number of whole + fractional seconds since some fixed point in " + " time. The clock is guaranteed to be non-decreasing in real time.\n" + "- :cputime: Return the CPU time consumed by this process (i.e. all threads in the process)\n") { janet_sandbox_assert(JANET_SANDBOX_HRTIME); - janet_fixarity(argc, 0); - (void) argv; + janet_arity(argc, 0, 1); + enum JanetTimeSource source = JANET_TIME_REALTIME; + if (argc == 1) { + JanetKeyword sourcestr = janet_getkeyword(argv, 0); + if (janet_cstrcmp(sourcestr, "realtime") == 0) { + source = JANET_TIME_REALTIME; + } else if (janet_cstrcmp(sourcestr, "monotonic") == 0) { + source = JANET_TIME_MONOTONIC; + } else if (janet_cstrcmp(sourcestr, "cputime") == 0) { + source = JANET_TIME_CPUTIME; + } else { + janet_panicf("expected :real-time, :monotonic, or :cputime, got %v", argv[0]); + } + } struct timespec tv; - if (janet_gettime(&tv)) janet_panic("could not get time"); + if (janet_gettime(&tv, source)) janet_panic("could not get time"); double dtime = tv.tv_sec + (tv.tv_nsec / 1E9); return janet_wrap_number(dtime); } diff --git a/src/core/util.c b/src/core/util.c index 4e7f8c22..78d51336 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -901,8 +901,20 @@ int janet_gettime(struct timespec *spec) { return 0; } #else -int janet_gettime(struct timespec *spec) { - return clock_gettime(CLOCK_MONOTONIC, spec); +int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { + clockid_t cid = JANET_TIME_REALTIME; + switch (source) { + case JANET_TIME_REALTIME: + cid = CLOCK_REALTIME; + break; + case JANET_TIME_MONOTONIC: + cid = CLOCK_MONOTONIC; + break; + case JANET_TIME_CPUTIME: + cid = CLOCK_PROCESS_CPUTIME_ID; + break; + } + return clock_gettime(cid, spec); } #endif #endif diff --git a/src/core/util.h b/src/core/util.h index b8f9cc90..3fe7b858 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -126,7 +126,12 @@ void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetReg /* Clock gettime */ #ifdef JANET_GETTIME -int janet_gettime(struct timespec *spec); +enum JanetTimeSource { + JANET_TIME_REALTIME, + JANET_TIME_MONOTONIC, + JANET_TIME_CPUTIME +}; +int janet_gettime(struct timespec *spec, enum JanetTimeSource source); #endif /* strdup */ From e8e5f66f4cd2d5ae5fc2594662806a7f05407831 Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Sat, 20 May 2023 12:00:15 +0200 Subject: [PATCH 3/5] Implement janet_gettime() for win32 and macos; need testing --- src/core/os.c | 6 ++-- src/core/util.c | 79 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/core/os.c b/src/core/os.c index 7a932f86..bf5673b7 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -1280,8 +1280,8 @@ JANET_CORE_FN(os_time, JANET_CORE_FN(os_clock, "(os/clock &opt source)", "Return the number of whole + fractional seconds of the requested clock source.\n\n" - "The `source` argument is the identifier of the particular clock source to use, when not " - "specified the default is `:realtime`:\n" + "The `source` argument selects the clock source to use, when not specified the default " + "is `:realtime`:\n" "- :realtime: Return the real (i.e., wall-clock) time. This clock is affected by discontinuous " " jumps in the system time\n" "- :monotonic: Return the number of whole + fractional seconds since some fixed point in " @@ -1299,7 +1299,7 @@ JANET_CORE_FN(os_clock, } else if (janet_cstrcmp(sourcestr, "cputime") == 0) { source = JANET_TIME_CPUTIME; } else { - janet_panicf("expected :real-time, :monotonic, or :cputime, got %v", argv[0]); + janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]); } } struct timespec tv; diff --git a/src/core/util.c b/src/core/util.c index 78d51336..623ef57e 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -875,44 +875,69 @@ int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffe /* Clock shims for various platforms */ #ifdef JANET_GETTIME #ifdef JANET_WINDOWS -int janet_gettime(struct timespec *spec) { - FILETIME ftime; - GetSystemTimeAsFileTime(&ftime); - int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32); - /* Windows epoch is January 1, 1601 apparently */ - wintime -= 116444736000000000LL; - spec->tv_sec = wintime / 10000000LL; - /* Resolution is 100 nanoseconds. */ - spec->tv_nsec = wintime % 10000000LL * 100; +#include +int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { + if (source == JANET_TIME_REALTIME) { + FILETIME ftime; + GetSystemTimeAsFileTime(&ftime); + int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32); + /* Windows epoch is January 1, 1601 apparently */ + wintime -= 116444736000000000LL; + spec->tv_sec = wintime / 10000000LL; + /* Resolution is 100 nanoseconds. */ + spec->tv_nsec = wintime % 10000000LL * 100; + } else if (source == JANET_TIME_MONOTONIC) { + LARGE_INTEGER count; + LARGE_INTEGER perf_freq; + QueryPerformanceCounter(&count); + QueryPerformanceFrequency(&perf_freq); + spec->tv_sec = count.QuadPart / perf_freq.QuadPart; + spec->tv_nsec = (count.QuadPart % perf_freq.QuadPart) * 1000000000 / perf_freq.QuadPart; + } else if (source == JANET_TIME_CPUTIME) { + float tmp = clock(); + spec->tv_sec = tmp / CLOCKS_PER_SEC; + spec->tv_nsec = (tmp - spec->tv_sec * CLOCKS_PER_SEC) * 1e9 / CLOCKS_PER_SEC; + } return 0; } /* clock_gettime() wasn't available on Mac until 10.12. */ #elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12) #include #include -int janet_gettime(struct timespec *spec) { - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - spec->tv_sec = mts.tv_sec; - spec->tv_nsec = mts.tv_nsec; +int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { + if (source == JANET_TIME_REALTIME) { + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + spec->tv_sec = mts.tv_sec; + spec->tv_nsec = mts.tv_nsec; + } else if (source == JANET_TIME_MONOTONIC) { + clock_serv_t cclock; + int nsecs; + mach_msg_type_number_t count; + host_get_clock_service(mach_host_self(), clock, &cclock); + clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count); + mach_port_deallocate(mach_task_self(), cclock); + clock_getres(CLOCK_MONOTONIC, spec); + } + if (source == JANET_TIME_CPUTIME) { + clock_t tmp = clock(); + spec->tv_sec = tmp; + spec->tv_nsec = (tmp - spec->tv_sec) * 1.0e9; + } return 0; } #else int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { clockid_t cid = JANET_TIME_REALTIME; - switch (source) { - case JANET_TIME_REALTIME: - cid = CLOCK_REALTIME; - break; - case JANET_TIME_MONOTONIC: - cid = CLOCK_MONOTONIC; - break; - case JANET_TIME_CPUTIME: - cid = CLOCK_PROCESS_CPUTIME_ID; - break; + if (source == JANET_TIME_REALTIME) { + cid = CLOCK_REALTIME; + } else if (source == JANET_TIME_MONOTONIC) { + cid = CLOCK_MONOTONIC; + } else if (source == JANET_TIME_CPUTIME) { + cid = CLOCK_PROCESS_CPUTIME_ID; } return clock_gettime(cid, spec); } From 80db6821097f34a59758088797ada8667896603a Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Sat, 20 May 2023 13:55:43 +0200 Subject: [PATCH 4/5] Added tests for os/clock --- test/suite0007.janet | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/suite0007.janet b/test/suite0007.janet index c60a9678..f8f74bea 100644 --- a/test/suite0007.janet +++ b/test/suite0007.janet @@ -333,4 +333,29 @@ (assert (pos? (length (gensym))) "gensym not empty, regression #753") + +# os/clock + +(defmacro measure-time [clocks & body] + (def t1 (gensym)) + (def t2 (gensym)) + ~(do + (def ,t1 (map |(os/clock $) ,clocks)) + ,;body + (def ,t2 (map |(os/clock $) ,clocks)) + (zipcoll ,clocks [ (- (,t2 0) (,t1 0)) (- (,t2 1) (,t1 1)) (- (,t2 2) (,t1 2))])) +) + +# Spin for 0.1 seconds +(def dt (measure-time [:realtime :monotonic :cputime] + (def t1 (os/clock :monotonic)) + (while (< (- (os/clock :monotonic) t1) 0.1) true))) +(assert (> (dt :monotonic) 0.10)) +(assert (> (dt :cputime) 0.05)) + +# Sleep for 0.1 seconds +(def dt (measure-time [:realtime :monotonic :cputime] (os/sleep 0.1))) +(assert (> (dt :monotonic) 0.10)) +(assert (< (dt :cputime) 0.05)) + (end-suite) From 30c47d685dee8628cf45c22dcff0db803566442e Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Sat, 20 May 2023 14:16:36 +0200 Subject: [PATCH 5/5] Fixed :cputime because msdn does not implement clock() properly --- src/core/util.c | 10 ++++++---- test/suite0007.janet | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/core/util.c b/src/core/util.c index 623ef57e..a699965e 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -892,11 +892,13 @@ int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { QueryPerformanceCounter(&count); QueryPerformanceFrequency(&perf_freq); spec->tv_sec = count.QuadPart / perf_freq.QuadPart; - spec->tv_nsec = (count.QuadPart % perf_freq.QuadPart) * 1000000000 / perf_freq.QuadPart; + spec->tv_nsec = (long)((count.QuadPart % perf_freq.QuadPart) * 1000000000 / perf_freq.QuadPart); } else if (source == JANET_TIME_CPUTIME) { - float tmp = clock(); - spec->tv_sec = tmp / CLOCKS_PER_SEC; - spec->tv_nsec = (tmp - spec->tv_sec * CLOCKS_PER_SEC) * 1e9 / CLOCKS_PER_SEC; + FILETIME creationTime, exitTime, kernelTime, userTime; + GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime); + int64_t tmp = ((int64_t)userTime.dwHighDateTime << 32) + userTime.dwLowDateTime; + spec->tv_sec = tmp / 10000000LL; + spec->tv_nsec = tmp % 10000000LL * 100; } return 0; } diff --git a/test/suite0007.janet b/test/suite0007.janet index f8f74bea..e59b049c 100644 --- a/test/suite0007.janet +++ b/test/suite0007.janet @@ -334,16 +334,16 @@ (assert (pos? (length (gensym))) "gensym not empty, regression #753") -# os/clock +# os/clock. These tests might prove fragile under CI because they +# rely on measured time. We'll see. (defmacro measure-time [clocks & body] - (def t1 (gensym)) - (def t2 (gensym)) + (def [t1 t2] [(gensym) (gensym)]) ~(do (def ,t1 (map |(os/clock $) ,clocks)) ,;body (def ,t2 (map |(os/clock $) ,clocks)) - (zipcoll ,clocks [ (- (,t2 0) (,t1 0)) (- (,t2 1) (,t1 1)) (- (,t2 2) (,t1 2))])) + (zipcoll ,clocks (map |(- ;$) (map tuple ,t2 ,t1)))) ) # Spin for 0.1 seconds