From d07319f790eb38c867bd04a23de674e2393825ff Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Sun, 22 Mar 2015 20:20:28 +0100 Subject: Better support for poor os monotonic sources --- erts/aclocal.m4 | 4 +- erts/doc/src/erlang.xml | 9 + erts/emulator/Makefile.in | 3 +- erts/emulator/beam/erl_time.h | 17 +- erts/emulator/beam/erl_time_sup.c | 237 +++++++++++++++------ erts/emulator/beam/sys.h | 2 + .../sys/common/erl_os_monotonic_time_extender.c | 88 ++++++++ .../sys/common/erl_os_monotonic_time_extender.h | 65 ++++++ erts/emulator/sys/unix/erl_unix_sys.h | 7 +- erts/emulator/sys/unix/sys_time.c | 200 ++++++----------- erts/emulator/sys/win32/sys_time.c | 58 +++-- 11 files changed, 441 insertions(+), 249 deletions(-) create mode 100644 erts/emulator/sys/common/erl_os_monotonic_time_extender.c create mode 100644 erts/emulator/sys/common/erl_os_monotonic_time_extender.h diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 1e449bf874..6ba54e823e 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -745,9 +745,7 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, done ]) - AC_CHECK_FUNC(clock_getres) - - AC_CHECK_FUNC(gethrtime) + AC_CHECK_FUNCS([clock_getres gethrtime]) AC_CACHE_CHECK([for mach clock_get_time()], erl_cv_mach_clock_get_time, [ diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 3cbfd372ce..98c0ef5f81 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6449,6 +6449,15 @@ ok precision won't be higher than OsMonotonicTimeResolution.

+ {extended, Extended} +

Extended equals yes if + the range of time values has been extended; + otherwise, Extended equals no. The + range needs to be extended if Function + returns values that wrap fast. This typically + is the case when the return value is a 32-bit + value.

+ {parallel, Parallel}

Parallel equals yes if Function is called in parallel from multiple diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 560cab396d..658f1b861a 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -898,7 +898,8 @@ OS_OBJS += $(OBJDIR)/erl_mseg.o \ $(OBJDIR)/erl_mmap.o \ $(OBJDIR)/erl_$(ERLANG_OSTYPE)_sys_ddll.o \ $(OBJDIR)/erl_mtrace_sys_wrap.o \ - $(OBJDIR)/erl_sys_common_misc.o + $(OBJDIR)/erl_sys_common_misc.o \ + $(OBJDIR)/erl_os_monotonic_time_extender.o HIPE_ARCH64_OBJS=$(OBJDIR)/hipe_bif64.o diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 924b0b6091..35a1442dbd 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -219,6 +219,10 @@ erts_time_unit_conversion(Uint64 value, * it is assumed (and need) to be a power of 10. */ +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT < 1000*1000 +# error Compile time time unit needs to be at least 1000000 +#endif + #define ERTS_MONOTONIC_TIME_UNIT \ ((ErtsMonotonicTime) ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT) @@ -248,19 +252,6 @@ erts_time_unit_conversion(Uint64 value, #define ERTS_USEC_TO_MONOTONIC__(USEC) ((ErtsMonotonicTime) (USEC)) #define ERTS_NSEC_TO_MONOTONIC__(NSEC) (((ErtsMonotonicTime) (NSEC))/1000) -#elif ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000 -/* Milli-second time unit */ - -#define ERTS_MONOTONIC_TO_SEC__(MSEC) ((USEC)/(1000)) -#define ERTS_MONOTONIC_TO_MSEC__(MSEC) (MSEC) -#define ERTS_MONOTONIC_TO_USEC__(MSEC) ((MSEC)*1000) -#define ERTS_MONOTONIC_TO_NSEC__(MSEC) ((MSEC)*(1000*1000)) - -#define ERTS_SEC_TO_MONOTONIC__(SEC) (((ErtsMonotonicTime) (SEC))*1000) -#define ERTS_MSEC_TO_MONOTONIC__(MSEC) ((ErtsMonotonicTime) (MSEC)) -#define ERTS_USEC_TO_MONOTONIC__(USEC) (((ErtsMonotonicTime) (USEC))/1000) -#define ERTS_NSEC_TO_MONOTONIC__(NSEC) (((ErtsMonotonicTime) (NSEC))/(1000*1000)) - #else #error Missing implementation for monotonic time unit #endif diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index b809fa8316..d961fae81a 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -21,6 +21,8 @@ * Support routines for the time */ +/* #define ERTS_TIME_CORRECTION_PRINT */ + #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -75,12 +77,12 @@ schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset); #else /* ARCH_64 */ -#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT <= 1000*1000 +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT <= 10*1000*1000 /* * Using micro second time unit or lower. Start at zero since * time will remain an immediate for a very long time anyway - * (18279 years in the micro second case)... + * (1827 years in the 10 micro second case)... */ #define ERTS_MONOTONIC_TIME_START ((ErtsMonotonicTime) 0) @@ -99,11 +101,16 @@ schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset); #endif /* ARCH_64 */ -#define ERTS_MONOTONIC_OFFSET_NATIVE ERTS_MONOTONIC_TIME_START -#define ERTS_MONOTONIC_OFFSET_NSEC ERTS_MONOTONIC_TO_NSEC__(ERTS_MONOTONIC_TIME_START) -#define ERTS_MONOTONIC_OFFSET_USEC ERTS_MONOTONIC_TO_USEC__(ERTS_MONOTONIC_TIME_START) -#define ERTS_MONOTONIC_OFFSET_MSEC ERTS_MONOTONIC_TO_MSEC__(ERTS_MONOTONIC_TIME_START) -#define ERTS_MONOTONIC_OFFSET_SEC ERTS_MONOTONIC_TO_SEC__(ERTS_MONOTONIC_TIME_START) +#define ERTS_MONOTONIC_OFFSET_NATIVE \ + (ERTS_MONOTONIC_TIME_START - ERTS_MONOTONIC_TIME_UNIT) +#define ERTS_MONOTONIC_OFFSET_NSEC \ + ERTS_MONOTONIC_TO_NSEC__(ERTS_MONOTONIC_OFFSET_NATIVE) +#define ERTS_MONOTONIC_OFFSET_USEC \ + ERTS_MONOTONIC_TO_USEC__(ERTS_MONOTONIC_OFFSET_NATIVE) +#define ERTS_MONOTONIC_OFFSET_MSEC \ + ERTS_MONOTONIC_TO_MSEC__(ERTS_MONOTONIC_OFFSET_NATIVE) +#define ERTS_MONOTONIC_OFFSET_SEC \ + ERTS_MONOTONIC_TO_SEC__(ERTS_MONOTONIC_OFFSET_NATIVE) #else /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ @@ -120,25 +127,6 @@ schedule_send_time_offset_changed_notifications(ErtsMonotonicTime new_offset); #endif /* ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT */ -#define ERTS_MONOTONIC_TO_SYS_TIME_VAL(TVP, MT) \ - do { \ - ErtsMonotonicTime sec__, usec__; \ - sec__ = ERTS_MONOTONIC_TO_SEC((MT)); \ - usec__ = ERTS_MONOTONIC_TO_USEC((MT)) - sec__*1000000; \ - ASSERT(usec__ < 1000000); \ - (TVP)->tv_sec = sec__; \ - (TVP)->tv_usec = usec__; \ - } while (0) - -#define ERTS_MAX_SYSTEM_TIME_DIFF ERTS_MSEC_TO_MONOTONIC(10) -#define ERTS_SYSTEM_TIME_DIFF_EXCEED_LIMIT(ESYSTIME, OSSYSTIME) \ - (((Uint64) (ESYSTIME)) - (((Uint64) (OSSYSTIME)) \ - - ERTS_MAX_SYSTEM_TIME_DIFF) \ - > 2*ERTS_MAX_SYSTEM_TIME_DIFF) - -#define ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF (ERTS_MAX_SYSTEM_TIME_DIFF/2) -#define ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(500) - struct time_sup_read_only__ { ErtsMonotonicTime (*get_time)(void); int correction; @@ -150,6 +138,7 @@ struct time_sup_read_only__ { char *os_monotonic_clock_id; int os_monotonic_locked; Uint64 os_monotonic_resolution; + Uint64 os_monotonic_extended; #endif #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT ErtsMonotonicTime start; @@ -161,6 +150,10 @@ struct time_sup_read_only__ { ErtsMonotonicTime sec; } start_offset; #endif + struct { + ErtsMonotonicTime large_diff; + ErtsMonotonicTime small_diff; + } adj; }; typedef struct { @@ -338,13 +331,77 @@ static ErtsMonotonicTime get_corrected_time(void) return calc_corrected_erl_mtime(os_mtime, cip, NULL); } +#ifdef ERTS_TIME_CORRECTION_PRINT + +static ERTS_INLINE void +print_correction(int change, + ErtsMonotonicTime sdiff, + ErtsMonotonicTime old_ecorr, + ErtsMonotonicTime old_dcorr, + ErtsMonotonicTime new_ecorr, + ErtsMonotonicTime new_dcorr, + Uint tmo) +{ + ErtsMonotonicTime usec_sdiff; + if (sdiff < 0) + usec_sdiff = -1*ERTS_MONOTONIC_TO_USEC(-1*sdiff); + else + usec_sdiff = ERTS_MONOTONIC_TO_USEC(sdiff); + + if (!change) + fprintf(stderr, + "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppm] : " + "tmo = %lld msec\r\n", + (long long) usec_sdiff, + (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, + (long long) (1000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) tmo); + else + fprintf(stderr, + "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppm] " + "-> [ec=%lld ppm, dc=%lld ppm] : tmo = %lld msec\r\n", + (long long) usec_sdiff, + (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, + (long long) (1000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) (1000000*new_ecorr) / ERTS_TCORR_ERR_UNIT, + (long long) (1000000*new_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (long long) tmo); + +} + +#endif + static void check_time_correction(void *unused) { +#ifndef ERTS_TIME_CORRECTION_PRINT +# define ERTS_PRINT_CORRECTION +#else +# ifdef ERTS_HAVE_CORRECTED_OS_MONOTONIC +# define ERTS_PRINT_CORRECTION \ + print_correction(set_new_correction, \ + sdiff, \ + cip->correction.error, \ + 0, \ + new_correction.error, \ + 0, \ + timeout) +# else +# define ERTS_PRINT_CORRECTION \ + print_correction(set_new_correction, \ + sdiff, \ + cip->correction.error, \ + cip->correction.drift, \ + new_correction.error, \ + new_correction.drift, \ + timeout) +# endif +#endif ErtsMonotonicCorrectionData cdata; ErtsMonotonicCorrection new_correction; ErtsMonotonicCorrectionInstance *cip; - ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, erl_stime, time_offset; + ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, + erl_stime, time_offset; Uint timeout; SysTimeval tod; int set_new_correction, begin_short_intervals = 0; @@ -377,8 +434,8 @@ check_time_correction(void *unused) new_correction = cip->correction; if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE - && (sdiff < -2*ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF - || 2*ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF < sdiff)) { + && (sdiff < -2*time_sup.r.o.adj.small_diff + || 2*time_sup.r.o.adj.small_diff < sdiff)) { /* System time diff exeeded limits; change time offset... */ time_offset -= sdiff; sdiff = 0; @@ -393,16 +450,16 @@ check_time_correction(void *unused) } } else if (cdata.curr.correction.error == 0) { - if (sdiff < -ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + if (sdiff < -time_sup.r.o.adj.small_diff) { set_new_correction = 1; - if (sdiff < -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + if (sdiff < -time_sup.r.o.adj.large_diff) new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; else new_correction.error = ERTS_TCORR_ERR_SMALL_ADJ; } - else if (sdiff > ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + else if (sdiff > time_sup.r.o.adj.small_diff) { set_new_correction = 1; - if (sdiff > ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + if (sdiff > time_sup.r.o.adj.large_diff) new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; else new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; @@ -414,16 +471,16 @@ check_time_correction(void *unused) else if (cdata.curr.correction.error > 0) { if (sdiff < 0) { if (cdata.curr.correction.error == ERTS_TCORR_ERR_LARGE_ADJ - || -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF <= sdiff) + || -time_sup.r.o.adj.large_diff <= sdiff) set_new_correction = 0; else { new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; set_new_correction = 1; } } - else if (sdiff > ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + else if (sdiff > time_sup.r.o.adj.small_diff) { set_new_correction = 1; - if (sdiff > ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + if (sdiff > time_sup.r.o.adj.large_diff) new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; else new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; @@ -436,7 +493,7 @@ check_time_correction(void *unused) else /* if (cdata.curr.correction.error < 0) */ { if (0 < sdiff) { if (cdata.curr.correction.error == -ERTS_TCORR_ERR_LARGE_ADJ - || sdiff <= ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + || sdiff <= time_sup.r.o.adj.large_diff) set_new_correction = 0; else { new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; @@ -444,9 +501,9 @@ check_time_correction(void *unused) } set_new_correction = 0; } - else if (sdiff < -ERTS_TIME_CORRECTION_SMALL_ADJ_DIFF) { + else if (sdiff < -time_sup.r.o.adj.small_diff) { set_new_correction = 1; - if (sdiff < -ERTS_TIME_CORRECTION_LARGE_ADJ_DIFF) + if (sdiff < -time_sup.r.o.adj.large_diff) new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; else new_correction.error = ERTS_TCORR_ERR_SMALL_ADJ; @@ -475,11 +532,13 @@ check_time_correction(void *unused) mtime_acc = ddp->acc.mon; stime_acc = ddp->acc.sys; - avg_drift_adj = ((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; + avg_drift_adj = (((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) + / mtime_acc); mtime_diff = os_mtime - old_os_mtime; stime_diff = os_stime - old_os_stime; - drift_adj = ((stime_diff - mtime_diff)*ERTS_MONOTONIC_TIME_UNIT) / mtime_diff; + drift_adj = (((stime_diff - mtime_diff)*ERTS_MONOTONIC_TIME_UNIT) + / mtime_diff); ix++; if (ix >= ERTS_DRIFT_INTERVALS) @@ -499,10 +558,11 @@ check_time_correction(void *unused) ddp->acc.sys = stime_acc; /* - * If calculated drift adjustment is if off by more than 20% from the - * average drift we interpret this as a discontinous leap in system - * time and ignore it. If it actually is a change in drift we will - * later detect this when the average drift change. + * If calculated drift adjustment is if off by more than 20% + * from the average drift we interpret this as a discontinous + * leap in system time and ignore it. If it actually is a + * change in drift we will later detect this when the average + * drift change. */ drift_adj_diff = avg_drift_adj - drift_adj; if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF @@ -512,7 +572,8 @@ check_time_correction(void *unused) } else { if (ddp->dirty_counter <= 0) { - drift_adj = ((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; + drift_adj = ((stime_acc - mtime_acc) + *ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; } if (ddp->dirty_counter >= 0) { if (ddp->dirty_counter == 0) { @@ -573,7 +634,9 @@ check_time_correction(void *unused) && time_sup.inf.c.parmon.cdata.short_check_interval) { timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK); } - + + ERTS_PRINT_CORRECTION; + if (set_new_correction) { erts_smp_rwmtx_rwlock(&time_sup.inf.c.parmon.rwmtx); @@ -610,6 +673,8 @@ check_time_correction(void *unused) NULL, NULL, timeout); + +#undef ERTS_PRINT_CORRECTION } #ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC @@ -618,7 +683,8 @@ static void init_check_time_correction(void *unused) { ErtsMonotonicDriftData *ddp; - ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff, stime_diff; + ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff, + stime_diff; int ix; SysTimeval tod; @@ -690,6 +756,7 @@ static void late_init_time_correction(void) { if (time_sup.inf.c.finalized_offset) { + erts_init_timer(&time_sup.inf.c.parmon.timer); erts_set_timer(&time_sup.inf.c.parmon.timer, #ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC @@ -792,6 +859,8 @@ void erts_init_sys_time_sup(void) = sys_init_time_res.os_monotonic_info.locked_use; time_sup.r.o.os_monotonic_resolution = sys_init_time_res.os_monotonic_info.resolution; + time_sup.r.o.os_monotonic_extended + = sys_init_time_res.os_monotonic_info.extended; #endif } @@ -799,7 +868,7 @@ int erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT - ErtsMonotonicTime abs_start; + ErtsMonotonicTime abs_native_offset, native_offset; #endif ASSERT(ERTS_MONOTONIC_TIME_MIN < ERTS_MONOTONIC_TIME_MAX); @@ -822,42 +891,68 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) time_sup.r.o.start /= ERTS_MONOTONIC_TIME_UNIT; time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT; time_sup.r.o.start += ERTS_MONOTONIC_TIME_UNIT; - abs_start = time_sup.r.o.start; + native_offset = time_sup.r.o.start - ERTS_MONOTONIC_TIME_UNIT; + native_offset = native_offset; #else /* ARCH_64 */ - if (ERTS_MONOTONIC_TIME_UNIT <= 1000*1000) - abs_start = time_sup.r.o.start = 0; + if (ERTS_MONOTONIC_TIME_UNIT <= 10*1000*1000) { + time_sup.r.o.start = 0; + native_offset = -ERTS_MONOTONIC_TIME_UNIT; + abs_native_offset = ERTS_MONOTONIC_TIME_UNIT; + } else { time_sup.r.o.start = ((ErtsMonotonicTime) MIN_SMALL); time_sup.r.o.start /= ERTS_MONOTONIC_TIME_UNIT; time_sup.r.o.start *= ERTS_MONOTONIC_TIME_UNIT; - abs_start = -1*time_sup.r.o.start; + native_offset = time_sup.r.o.start - ERTS_MONOTONIC_TIME_UNIT; + abs_native_offset = -1*native_offset; } #endif - time_sup.r.o.start_offset.native = time_sup.r.o.start; + time_sup.r.o.start_offset.native = (time_sup.r.o.start + - ERTS_MONOTONIC_TIME_UNIT); time_sup.r.o.start_offset.nsec = (ErtsMonotonicTime) - erts_time_unit_conversion((Uint64) abs_start, + erts_time_unit_conversion((Uint64) abs_native_offset, (Uint32) ERTS_MONOTONIC_TIME_UNIT, (Uint32) 1000*1000*1000); time_sup.r.o.start_offset.usec = (ErtsMonotonicTime) - erts_time_unit_conversion((Uint64) abs_start, + erts_time_unit_conversion((Uint64) abs_native_offset, (Uint32) ERTS_MONOTONIC_TIME_UNIT, (Uint32) 1000*1000); time_sup.r.o.start_offset.msec = (ErtsMonotonicTime) - erts_time_unit_conversion((Uint64) abs_start, + erts_time_unit_conversion((Uint64) abs_native_offset, (Uint32) ERTS_MONOTONIC_TIME_UNIT, (Uint32) 1000); time_sup.r.o.start_offset.sec = (ErtsMonotonicTime) - erts_time_unit_conversion((Uint64) abs_start, + erts_time_unit_conversion((Uint64) abs_native_offset, (Uint32) ERTS_MONOTONIC_TIME_UNIT, (Uint32) 1); - if (time_sup.r.o.start < 0) { + if (native_offset < 0) { time_sup.r.o.start_offset.nsec *= -1; time_sup.r.o.start_offset.usec *= -1; time_sup.r.o.start_offset.msec *= -1; time_sup.r.o.start_offset.sec *= -1; } +#endif + + time_sup.r.o.adj.large_diff = erts_time_sup__.r.o.monotonic_time_unit; + time_sup.r.o.adj.large_diff *= 50; + time_sup.r.o.adj.large_diff /= time_sup.r.o.os_monotonic_resolution; + if (time_sup.r.o.adj.large_diff < ERTS_MSEC_TO_MONOTONIC(5)) + time_sup.r.o.adj.large_diff = ERTS_MSEC_TO_MONOTONIC(5); + time_sup.r.o.adj.small_diff = time_sup.r.o.adj.large_diff/10; + +#ifdef ERTS_TIME_CORRECTION_PRINT + fprintf(stderr, "start = %lld\n\r", (long long) ERTS_MONOTONIC_TIME_START); + fprintf(stderr, "native offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_NATIVE); + fprintf(stderr, "nsec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_NSEC); + fprintf(stderr, "usec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_USEC); + fprintf(stderr, "msec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_MSEC); + fprintf(stderr, "sec offset = %lld\n\r", (long long) ERTS_MONOTONIC_OFFSET_SEC); + fprintf(stderr, "large diff = %lld usec\r\n", + (long long) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.large_diff)); + fprintf(stderr, "small diff = %lld usec\r\n", + (long long) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.small_diff)); #endif if (ERTS_MONOTONIC_TIME_UNIT < ERTS_CLKTCK_RESOLUTION) @@ -878,6 +973,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) time_sup.r.o.moffset = -1*time_sup.inf.c.minit; offset = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); offset += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + offset -= ERTS_MONOTONIC_TIME_UNIT; init_time_offset(offset); rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ; @@ -897,7 +993,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) cdatap->curr.correction.drift = 0; #endif cdatap->curr.correction.error = 0; - cdatap->curr.erl_mtime = 0; + cdatap->curr.erl_mtime = ERTS_MONOTONIC_TIME_UNIT; cdatap->curr.os_mtime = time_sup.inf.c.minit; cdatap->last_check = time_sup.inf.c.minit; cdatap->short_check_interval = ERTS_INIT_SHORT_INTERVAL_COUNTER; @@ -910,9 +1006,10 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { ErtsMonotonicTime stime, offset; time_sup.r.o.get_time = get_not_corrected_time; + sys_gettimeofday(&time_sup.inf.c.inittv); stime = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec); - stime += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); - offset = stime; + stime += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec); + offset = stime - ERTS_MONOTONIC_TIME_UNIT; time_sup.inf.c.not_corrected_moffset = offset; init_time_offset(offset); time_sup.f.c.last_not_corrected_time = 0; @@ -937,6 +1034,7 @@ erts_late_init_time_sup(void) if (time_sup.r.o.get_time == get_corrected_time) late_init_time_correction(); #endif + erts_late_sys_init_time(); } ErtsTimeWarpMode erts_time_warp_mode(void) @@ -1737,7 +1835,7 @@ make_time_val(Process *c_p, ErtsMonotonicTime time_val) Eterm erts_get_monotonic_start_time(struct process *c_p) { - return make_time_val(c_p, ERTS_MONOTONIC_OFFSET_NATIVE); + return make_time_val(c_p, ERTS_MONOTONIC_TIME_START); } static Eterm @@ -1747,8 +1845,8 @@ bld_monotonic_time_source(Uint **hpp, Uint *szp, Sint64 os_mtime) return NIL; #else int i = 0; - Eterm k[5]; - Eterm v[5]; + Eterm k[6]; + Eterm v[6]; if (time_sup.r.o.os_monotonic_disable) return NIL; @@ -1761,10 +1859,11 @@ bld_monotonic_time_source(Uint **hpp, Uint *szp, Sint64 os_mtime) v[i++] = erts_bld_atom(hpp, szp, time_sup.r.o.os_monotonic_clock_id); } - if (time_sup.r.o.os_monotonic_resolution) { - k[i] = erts_bld_atom(hpp, szp, "resolution"); - v[i++] = erts_bld_uint64(hpp, szp, time_sup.r.o.os_monotonic_resolution); - } + k[i] = erts_bld_atom(hpp, szp, "resolution"); + v[i++] = erts_bld_uint64(hpp, szp, time_sup.r.o.os_monotonic_resolution); + + k[i] = erts_bld_atom(hpp, szp, "extended"); + v[i++] = time_sup.r.o.os_monotonic_extended ? am_yes : am_no; k[i] = erts_bld_atom(hpp, szp, "parallel"); v[i++] = time_sup.r.o.os_monotonic_locked ? am_no : am_yes; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index a108e819c6..d83bdf8d31 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -692,6 +692,7 @@ typedef struct { char *func; char *clock_id; int locked_use; + int extended; } os_monotonic_info; } ErtsSysInitTimeResult; @@ -700,6 +701,7 @@ typedef struct { extern void erts_init_sys_time_sup(void); extern void sys_init_time(ErtsSysInitTimeResult *); +extern void erts_late_sys_init_time(void); extern void erts_deliver_time(void); extern void erts_time_remaining(SysTimeval *); extern int erts_init_time_sup(int, ErtsTimeWarpMode); diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.c b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c new file mode 100644 index 0000000000..f3633b7267 --- /dev/null +++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.c @@ -0,0 +1,88 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2015. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "erl_os_monotonic_time_extender.h" + +#ifdef USE_THREADS + +static void *os_monotonic_time_extender(void *vstatep) +{ + ErtsOsMonotonicTimeExtendState *state = (ErtsOsMonotonicTimeExtendState *) vstatep; + long sleep_time = state->check_interval*1000; + Uint32 (*raw_os_mtime)(void) = state->raw_os_monotonic_time; + Uint32 last_msb = 0; + + while (1) { + Uint32 msb = (*raw_os_mtime)() & (((Uint32) 1) << 31); + + if (msb != last_msb) { + int ix = ((int) (last_msb >> 31)) & 1; + Uint32 xtnd = (Uint32) erts_atomic32_read_nob(&state->extend[ix]); + erts_atomic32_set_nob(&state->extend[ix], (erts_aint32_t) (xtnd + 1)); + last_msb = msb; + } + erts_milli_sleep(sleep_time); + } + + erl_exit(ERTS_ABORT_EXIT, "os_monotonic_time_extender thread terminating"); + return NULL; +} + +static erts_tid_t os_monotonic_extender_tid; +#endif + +void +erts_init_os_monotonic_time_extender(ErtsOsMonotonicTimeExtendState *statep, + Uint32 (*raw_os_monotonic_time)(void), + int check_seconds) +{ +#ifdef USE_THREADS + statep->raw_os_monotonic_time = raw_os_monotonic_time; + erts_atomic32_init_nob(&statep->extend[0], (erts_aint32_t) 0); + erts_atomic32_init_nob(&statep->extend[1], (erts_aint32_t) 0); + statep->check_interval = check_seconds; + +#else + statep->extend[0] = (Uint32) 0; + statep->extend[1] = (Uint32) 0; + statep->last_msb = (ErtsMonotonicTime) 0; +#endif +} + +void +erts_late_init_os_monotonic_time_extender(ErtsOsMonotonicTimeExtendState *statep) +{ +#ifdef USE_THREADS + erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER; + thr_opts.detached = 1; + thr_opts.suggested_stack_size = 4; + +#if 0 + thr_opts.name = "os_monotonic_time_extender"; +#endif + + erts_thr_create(&os_monotonic_extender_tid, + os_monotonic_time_extender, + (void*) statep, + &thr_opts); +#endif +} diff --git a/erts/emulator/sys/common/erl_os_monotonic_time_extender.h b/erts/emulator/sys/common/erl_os_monotonic_time_extender.h new file mode 100644 index 0000000000..0f9e7c86ae --- /dev/null +++ b/erts/emulator/sys/common/erl_os_monotonic_time_extender.h @@ -0,0 +1,65 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2015. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifndef ERL_OS_MONOTONIC_TIME_EXTENDER_H__ +#define ERL_OS_MONOTONIC_TIME_EXTENDER_H__ + +#include "sys.h" +#include "erl_threads.h" + +typedef struct { +#ifdef USE_THREADS + Uint32 (*raw_os_monotonic_time)(void); + erts_atomic32_t extend[2]; + int check_interval; +#else + Uint32 extend[2]; + ErtsMonotonicTime last_msb; +#endif +} ErtsOsMonotonicTimeExtendState; + +#ifdef USE_THREADS +# define ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(S, RT) ((void) 1) +# define ERTS_EXTEND_OS_MONOTONIC_TIME(S, RT) \ + ((((ErtsMonotonicTime) \ + erts_atomic32_read_nob(&((S)->extend[((int) ((RT) >> 31)) & 1]))) \ + << 32) \ + + (RT)) +#else +# define ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(S, RT) \ + do { \ + Uint32 msb__ = (RT) & (((Uint32) 1) << 31); \ + if (msb__ != (S)->last_msb) { \ + int ix__ = ((int) ((S)->last_msb >> 31)) & 1; \ + (S)->extend[ix__]++; \ + (S)->last_msb = msb; \ + } \ + } while (0) +# define ERTS_EXTEND_OS_MONOTONIC_TIME(S, RT) \ + ((((ErtsMonotonicTime) (S)->extend[((int) ((RT) >> 31)) & 1]) << 32) + (RT)) +#endif + +void +erts_init_os_monotonic_time_extender(ErtsOsMonotonicTimeExtendState *statep, + Uint32 (*raw_os_monotonic_time)(void), + int check_seconds); +void +erts_late_init_os_monotonic_time_extender(ErtsOsMonotonicTimeExtendState *statep); + +#endif diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 5417bb2687..303ebeee7c 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -206,12 +206,9 @@ ErtsMonotonicTime erts_os_monotonic_time(void); || defined(OS_MONOTONIC_TIME_USING_TIMES) #if defined(OS_MONOTONIC_TIME_USING_TIMES) +/* Time unit determined at runtime... */ # undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT -# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000) -# define ERTS_HAVE_ERTS_OS_TIME_OFFSET_FINALIZE 1 -void erts_os_time_offset_finalize(void); -# define ERTS_HAVE_ERTS_OS_MONOTONIC_TIME_INIT -void erts_os_monotonic_time_init(void); +# define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT 0 #endif ErtsMonotonicTime erts_os_monotonic_time(void); diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index 9fdb1930b7..9db727b111 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -33,6 +33,7 @@ #include "sys.h" #include "global.h" +#include "erl_os_monotonic_time_extender.h" #ifdef NO_SYSCONF # define TICKS_PER_SEC() HZ @@ -55,36 +56,19 @@ #undef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ #undef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ +#undef ERTS_SYS_TIME_INTERNAL_STATE_READ_MOSTLY__ #if defined(OS_MONOTONIC_TIME_USING_TIMES) -#define ERTS_WRAP_SYS_TIMES 1 -#define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ -#define ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ - -/* - * Not sure there is a need to use times() anymore, perhaps drop - * support for this soon... - * - * sys_times() might need to be wrapped and the values shifted (right) - * a bit to cope with faster ticks, this has to be taken care - * of dynamically to start with, a special version that uses - * the times() return value as a high resolution timer can be made - * to fully utilize the faster ticks, like on windows, but for now, we'll - * settle with this silly workaround - */ -#ifdef ERTS_WRAP_SYS_TIMES -static clock_t sys_times_wrap(void); -#define KERNEL_TICKS() (sys_times_wrap() & \ - ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) -#define ERTS_KERNEL_TICK_TO_USEC(TCKS) (((TCKS)*(1000*1000)) \ - / internal_state.r.o.ticks_per_sec_wrap) -#else +static Uint32 +get_tick_count(void) +{ + struct tms unused; + return (Uint32) times(&unused); +} -#define KERNEL_TICKS() (sys_times(&internal_state.w.f.dummy_tms) & \ - ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) -#define ERTS_KERNEL_TICK_TO_USEC(TCKS) (((TCKS)*(1000*1000))/SYS_CLK_TCK) -#endif +#define ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ +#define ERTS_SYS_TIME_INTERNAL_STATE_READ_MOSTLY__ #endif @@ -109,8 +93,15 @@ ErtsMonotonicTime clock_gettime_monotonic_verified(void); #ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ struct sys_time_internal_state_read_only__ { #if defined(OS_MONOTONIC_TIME_USING_TIMES) - int ticks_bsr; - int ticks_per_sec_wrap; + int times_shift; +#endif +}; +#endif + +#ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_MOSTLY__ +struct sys_time_internal_state_read_mostly__ { +#if defined(OS_MONOTONIC_TIME_USING_TIMES) + ErtsOsMonotonicTimeExtendState os_mtime_xtnd; #endif }; #endif @@ -121,15 +112,6 @@ struct sys_time_internal_state_write_freq__ { #if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) ErtsMonotonicTime last_delivered; #endif -#if defined(OS_MONOTONIC_TIME_USING_TIMES) - ErtsMonotonicTime last_tick_count; - ErtsMonotonicTime last_tick_wrap_count; - ErtsMonotonicTime last_tick_monotonic_time; - ErtsMonotonicTime last_timeofday_usec; -#ifndef ERTS_WRAP_SYS_TIMES - SysTimes dummy_tms; -#endif -#endif }; #endif @@ -144,6 +126,14 @@ static struct { * ASSUMED_CACHE_LINE_SIZE]; } r; #endif +#ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_MOSTLY__ + union { + struct sys_time_internal_state_read_mostly__ m; + char align__[(((sizeof(struct sys_time_internal_state_read_mostly__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } wr; +#endif #ifdef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ union { struct sys_time_internal_state_write_freq__ f; @@ -193,8 +183,6 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_info.func = "gethrtime"; #elif defined(OS_MONOTONIC_TIME_USING_TIMES) init_resp->os_monotonic_info.func = "times"; - init_resp->os_monotonic_info.locked_use = 1; - init_resp->os_monotonic_info.resolution = TICKS_PER_SEC(); #else # error Unknown erts_os_monotonic_time() implementation #endif @@ -247,7 +235,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) #endif /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */ +#ifdef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT init_resp->os_monotonic_time_unit = ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT; +#endif init_resp->sys_clock_resolution = SYS_CLOCK_RESOLUTION; /* @@ -258,38 +248,40 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) erl_exit(ERTS_ABORT_EXIT, "Can't get clock ticks/sec\n"); #if defined(OS_MONOTONIC_TIME_USING_TIMES) +#if ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT +# error Time unit is supposed to be determined at runtime... +#endif + { + ErtsMonotonicTime resolution = erts_sys_time_data__.r.o.ticks_per_sec; + ErtsMonotonicTime time_unit = resolution; + int shift = 0; - if (erts_sys_time_data__.r.o.ticks_per_sec >= 1000) { - /* Workaround for beta linux kernels, need to be done in runtime - to make erlang run on both 2.4 and 2.5 kernels. In the future, - the kernel ticks might as - well be used as a high res timer instead, but that's for when the - majority uses kernels with HZ == 1024 */ - internal_state.r.o.ticks_bsr = 3; - } else { - internal_state.r.o.ticks_bsr = 0; - } + while (time_unit < 1000*1000) { + time_unit <<= 1; + shift++; + } - internal_state.r.o.ticks_per_sec_wrap - = (erts_sys_time_data__.r.o.ticks_per_sec - >> internal_state.r.o.ticks_bsr); + init_resp->os_monotonic_info.resolution = resolution; + init_resp->os_monotonic_time_unit = time_unit; + init_resp->os_monotonic_info.extended = 1; + internal_state.r.o.times_shift = shift; - erts_smp_mtx_init(&internal_state.w.f.mtx, "os_monotonic_time"); - internal_state.w.f.last_tick_count = KERNEL_TICKS(); - internal_state.w.f.last_tick_wrap_count = 0; - internal_state.w.f.last_tick_monotonic_time - = ERTS_KERNEL_TICK_TO_USEC(internal_state.w.f.last_tick_count); - { - SysTimeval tv; - sys_gettimeofday(&tv); - internal_state.w.f.last_timeofday_usec = tv.tv_sec*(1000*1000); - internal_state.w.f.last_timeofday_usec += tv.tv_usec; + erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd, + get_tick_count, + (1 << 29) / resolution); } - #endif /* defined(OS_MONOTONIC_TIME_USING_TIMES) */ } +void +erts_late_sys_init_time(void) +{ +#if defined(OS_MONOTONIC_TIME_USING_TIMES) + erts_late_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd); +#endif +} + #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) static ERTS_INLINE ErtsMonotonicTime @@ -379,86 +371,14 @@ ErtsMonotonicTime erts_os_monotonic_time(void) #elif defined(OS_MONOTONIC_TIME_USING_TIMES) -static clock_t sys_times_wrap(void) -{ - SysTimes dummy; - clock_t result = (sys_times(&dummy) >> internal_state.r.o.ticks_bsr); - return result; -} - -void -erts_os_time_offset_finalize(void) -{ - erts_smp_mtx_lock(&internal_state.w.f.mtx); - internal_state.w.f.last_tick_wrap_count = 0; - erts_smp_mtx_unlock(&internal_state.w.f.mtx); -} - -#define ERTS_TIME_EXCEED_TICK_LIMIT(SYS_TIME, TCK_TIME) \ - (((Uint64) (SYS_TIME)) - (((Uint64) (TCK_TIME)) \ - - ERTS_KERNEL_TICK_TO_USEC(1)) \ - > ERTS_KERNEL_TICK_TO_USEC(2)) - -/* Returns monotonic time in micro seconds */ ErtsMonotonicTime erts_os_monotonic_time(void) { - SysTimeval tv; - ErtsMonotonicTime res; - ErtsMonotonicTime tick_count; - ErtsMonotonicTime tick_count_usec; - ErtsMonotonicTime tick_monotonic_time; - ErtsMonotonicTime timeofday_usec; - ErtsMonotonicTime timeofday_diff_usec; - - erts_smp_mtx_lock(&internal_state.w.f.mtx); - - tick_count = (ErtsMonotonicTime) KERNEL_TICKS(); - sys_gettimeofday(&tv); - - if (internal_state.w.f.last_tick_count > tick_count) { - internal_state.w.f.last_tick_wrap_count - += (((ErtsMonotonicTime) 1) << ((sizeof(clock_t) * 8) - 1)); - } - internal_state.w.f.last_tick_count = tick_count; - tick_count += internal_state.w.f.last_tick_wrap_count; - - tick_count_usec = ERTS_KERNEL_TICK_TO_USEC(tick_count); - - timeofday_usec = (ErtsMonotonicTime) tv.tv_sec*(1000*1000); - timeofday_usec += (ErtsMonotonicTime) tv.tv_usec; - timeofday_diff_usec = timeofday_usec; - timeofday_diff_usec -= internal_state.w.f.last_timeofday_usec; - internal_state.w.f.last_timeofday_usec = timeofday_usec; - - if (timeofday_diff_usec < 0) { - /* timeofday jumped backwards use tick count only... */ - tick_monotonic_time = tick_count_usec; - } - else { - /* Use time diff from of timeofday if not off by too much... */ - tick_monotonic_time = internal_state.w.f.last_tick_monotonic_time; - tick_monotonic_time += timeofday_diff_usec; - - if (ERTS_TIME_EXCEED_TICK_LIMIT(tick_monotonic_time, tick_count_usec)) { - /* - * Value off by more than one tick from tick_count, i.e. - * timofday leaped one way or the other. We use - * tick_count_usec as is instead and unfortunately - * get lousy precision. - */ - tick_monotonic_time = tick_count_usec; - } - } - - if (internal_state.w.f.last_tick_monotonic_time < tick_monotonic_time) - internal_state.w.f.last_tick_monotonic_time = tick_monotonic_time; - - res = internal_state.w.f.last_tick_monotonic_time; - - erts_smp_mtx_unlock(&internal_state.w.f.mtx); - - return res; + Uint32 ticks = get_tick_count(); + ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks); + return ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks) << internal_state.r.o.times_shift; } #endif /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */ diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index 3a10125c81..5fa575fa3b 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -25,6 +25,7 @@ #endif #include "sys.h" #include "assert.h" +#include "erl_os_monotonic_time_extender.h" #define LL_LITERAL(X) ERTS_I64_LITERAL(X) @@ -80,6 +81,10 @@ struct sys_time_internal_state_read_only__ { BOOL (WINAPI *pQueryPerformanceCounter)(LARGE_INTEGER *); }; +struct sys_time_internal_state_read_mostly__ { + ErtsOsMonotonicTimeExtendState os_mtime_xtnd; +}; + struct sys_time_internal_state_write_freq__ { erts_smp_mtx_t mtime_mtx; ULONGLONG wrap; @@ -93,6 +98,12 @@ __declspec(align(ASSUMED_CACHE_LINE_SIZE)) struct { / ASSUMED_CACHE_LINE_SIZE) + 1) * ASSUMED_CACHE_LINE_SIZE]; } r; + union { + struct sys_time_internal_state_read_mostly__ m; + char align__[(((sizeof(struct sys_time_internal_state_read_mostly__) - 1) + / ASSUMED_CACHE_LINE_SIZE) + 1) + * ASSUMED_CACHE_LINE_SIZE]; + } wr; union { struct sys_time_internal_state_write_freq__ f; char align__[(((sizeof(struct sys_time_internal_state_write_freq__) - 1) @@ -114,29 +125,27 @@ os_monotonic_time_qpc(void) return (ErtsMonotonicTime) pc.QuadPart; } +static Uint32 +get_tick_count(void) +{ + return (Uint32) GetTickCount(); +} + static ErtsMonotonicTime os_monotonic_time_gtc32(void) { - ULONGLONG res, ticks; - - erts_smp_mtx_lock(&internal_state.w.f.mtime_mtx); - - ticks = (ULONGLONG) (GetTickCount() & 0x7FFFFFFF); - if (ticks < internal_state.w.f.last_tick_count) - internal_state.w.f.wrap += (ULONGLONG) LL_LITERAL(1) << 31; - internal_state.w.f.last_tick_count = ticks; - res = ticks + internal_state.w.f.wrap; - - erts_smp_mtx_unlock(&internal_state.w.f.mtime_mtx); - - return (ErtsMonotonicTime) res*1000; + Uint32 ticks = (Uint32) GetTickCount(); + ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + tick_count); + return ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd, + ticks) << 10; } static ErtsMonotonicTime os_monotonic_time_gtc64(void) { ULONGLONG ticks = (*internal_state.r.o.pGetTickCount64)(); - return (ErtsMonotonicTime) ticks*1000; + return (ErtsMonotonicTime) ticks << 10; } /* @@ -163,9 +172,14 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_info.func = "GetTickCount"; init_resp->os_monotonic_info.locked_use = 1; - init_resp->os_monotonic_info.resolution = 1000; - time_unit = (ErtsMonotonicTime) 1000*1000; + /* 10-16 ms resolution according to MicroSoft documentation */ + init_resp->os_monotonic_info.resolution = 100; /* 10 ms */ + time_unit = (ErtsMonotonicTime) (1000 << 10); os_mtime_func = os_monotonic_time_gtc32; + init_resp->os_monotonic_info.extended = 1; + erts_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd, + get_tick_count, + 60*60*24*7); /* Check once a week */ } else { int major, minor, build; @@ -184,8 +198,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) init_resp->os_monotonic_info.func = "GetTickCount64"; init_resp->os_monotonic_info.locked_use = 0; - init_resp->os_monotonic_info.resolution = 1000; - time_unit = (ErtsMonotonicTime) 1000*1000; + /* 10-16 ms resolution according to MicroSoft documentation */ + init_resp->os_monotonic_info.resolution = 100; /* 10 ms */ + time_unit = (ErtsMonotonicTime) (1000 << 10); os_mtime_func = os_monotonic_time_gtc64; } else { /* Vista or newer... */ @@ -235,6 +250,13 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) } +void +erts_late_sys_init_time(void) +{ + if (erts_sys_time_data__.r.o.os_monotonic_time == os_monotonic_time_gtc32) + erts_late_init_os_monotonic_time_extender(&internal_state.wr.m.os_mtime_xtnd); +} + /* Returns a switchtimes for DST as UTC filetimes given data from a TIME_ZONE_INFORMATION, see sys_localtime_r for usage. */ static void -- cgit v1.2.3