aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/aclocal.m44
-rw-r--r--erts/doc/src/erlang.xml9
-rw-r--r--erts/emulator/Makefile.in3
-rw-r--r--erts/emulator/beam/erl_time.h17
-rw-r--r--erts/emulator/beam/erl_time_sup.c237
-rw-r--r--erts/emulator/beam/sys.h2
-rw-r--r--erts/emulator/sys/common/erl_os_monotonic_time_extender.c88
-rw-r--r--erts/emulator/sys/common/erl_os_monotonic_time_extender.h65
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h7
-rw-r--r--erts/emulator/sys/unix/sys_time.c200
-rw-r--r--erts/emulator/sys/win32/sys_time.c58
11 files changed, 441 insertions, 249 deletions
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
<c>OsMonotonicTimeResolution</c>.</p></item>
+ <tag><c>{extended, Extended}</c></tag>
+ <item><p><c>Extended</c> equals <c>yes</c> if
+ the range of time values has been extended;
+ otherwise, <c>Extended</c> equals <c>no</c>. The
+ range needs to be extended if <c>Function</c>
+ returns values that wrap fast. This typically
+ is the case when the return value is a 32-bit
+ value.</p></item>
+
<tag><c>{parallel, Parallel}</c></tag>
<item><p><c>Parallel</c> equals <c>yes</c> if
<c>Function</c> 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,36 +891,42 @@ 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;
@@ -860,6 +935,26 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
#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)
ERTS_INTERNAL_ERROR("Too small monotonic time time unit");
@@ -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;
@@ -94,6 +99,12 @@ __declspec(align(ASSUMED_CACHE_LINE_SIZE)) struct {
* 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)
/ ASSUMED_CACHE_LINE_SIZE) + 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