aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/Makefile.in3
-rw-r--r--erts/emulator/beam/erl_bif_info.c2
-rw-r--r--erts/emulator/beam/erl_lock_count.c13
-rw-r--r--erts/emulator/beam/erl_time.h18
-rw-r--r--erts/emulator/beam/erl_time_sup.c431
-rw-r--r--erts/emulator/beam/sys.h13
-rw-r--r--erts/emulator/beam/time.c1
-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.h23
-rw-r--r--erts/emulator/sys/unix/sys.c12
-rw-r--r--erts/emulator/sys/unix/sys_time.c391
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h14
-rw-r--r--erts/emulator/sys/win32/sys_time.c185
14 files changed, 886 insertions, 373 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 1429a6cf2c..b4a17e76e7 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -888,7 +888,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_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 2eeebab9a3..b2658a1fd6 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -2123,6 +2123,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
}
} else if (ERTS_IS_ATOM_STR("os_monotonic_time_source", BIF_ARG_1)) {
BIF_RET(erts_monotonic_time_source(BIF_P));
+ } else if (ERTS_IS_ATOM_STR("os_system_time_source", BIF_ARG_1)) {
+ BIF_RET(erts_system_time_source(BIF_P));
} else if (ERTS_IS_ATOM_STR("time_correction", BIF_ARG_1)) {
BIF_RET(erts_has_time_correction() ? am_true : am_false);
} else if (ERTS_IS_ATOM_STR("start_time", BIF_ARG_1)) {
diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c
index ddeb56a6be..c6d8f4df95 100644
--- a/erts/emulator/beam/erl_lock_count.c
+++ b/erts/emulator/beam/erl_lock_count.c
@@ -104,16 +104,13 @@ static void lcnt_clear_stats(erts_lcnt_lock_stats_t *stats) {
}
static void lcnt_time(erts_lcnt_time_t *time) {
-#if 0 || defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT)
- ErtsMonotonicTime mtime = ERTS_MONOTONIC_TO_NSEC(erts_os_monotonic_time());
+ /*
+ * erts_sys_hrtime() is the highest resolution
+ * we could find, it may or may not be monotonic...
+ */
+ ErtsMonotonicTime mtime = erts_sys_hrtime();
time->s = (unsigned long) (mtime / 1000000000LL);
time->ns = (unsigned long) (mtime - 1000000000LL*time->s);
-#else
- SysTimeval tv;
- sys_gettimeofday(&tv);
- time->s = tv.tv_sec;
- time->ns = tv.tv_usec*1000LL;
-#endif
}
static void lcnt_time_diff(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_time_t *t0) {
diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h
index c9cda4d10e..cb7764addc 100644
--- a/erts/emulator/beam/erl_time.h
+++ b/erts/emulator/beam/erl_time.h
@@ -148,6 +148,7 @@ ErtsTimeOffsetState erts_finalize_time_offset(void);
struct process;
Eterm erts_get_monotonic_start_time(struct process *c_p);
Eterm erts_monotonic_time_source(struct process*c_p);
+Eterm erts_system_time_source(struct process*c_p);
#ifdef SYS_CLOCK_RESOLUTION
#define ERTS_CLKTCK_RESOLUTION ((ErtsMonotonicTime) (SYS_CLOCK_RESOLUTION*1000))
@@ -219,6 +220,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 +253,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..ef39f4b5f4 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,36 +127,23 @@ 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;
ErtsTimeWarpMode warp_mode;
#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
ErtsMonotonicTime moffset;
- int os_monotonic_disable;
- char *os_monotonic_func;
- char *os_monotonic_clock_id;
- int os_monotonic_locked;
- Uint64 os_monotonic_resolution;
+ int os_monotonic_time_disable;
+ char *os_monotonic_time_func;
+ char *os_monotonic_time_clock_id;
+ int os_monotonic_time_locked;
+ Uint64 os_monotonic_time_resolution;
+ Uint64 os_monotonic_time_extended;
+ char *os_system_time_func;
+ char *os_system_time_clock_id;
+ int os_system_time_locked;
+ Uint64 os_system_time_resolution;
+ Uint64 os_system_time_extended;
#endif
#if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
ErtsMonotonicTime start;
@@ -161,6 +155,10 @@ struct time_sup_read_only__ {
ErtsMonotonicTime sec;
} start_offset;
#endif
+ struct {
+ ErtsMonotonicTime large_diff;
+ ErtsMonotonicTime small_diff;
+ } adj;
};
typedef struct {
@@ -216,7 +214,7 @@ struct time_sup_infrequently_changed__ {
ErtsMonotonicTime minit;
#endif
int finalized_offset;
- SysTimeval inittv; /* Used everywhere, the initial time-of-day */
+ ErtsSystemTime sinit;
ErtsMonotonicTime not_corrected_moffset;
erts_atomic64_t offset;
};
@@ -249,10 +247,8 @@ ErtsTimeSupData erts_time_sup__ erts_align_attribute(ERTS_CACHE_LINE_SIZE);
erts_approx_time_t
erts_get_approx_time(void)
{
- SysTimeval tv;
- sys_gettimeofday(&tv);
-
- return (erts_approx_time_t) tv.tv_sec;
+ ErtsSystemTime stime = erts_os_system_time();
+ return (erts_approx_time_t) ERTS_MONOTONIC_TO_SEC(stime);
}
static ERTS_INLINE void
@@ -338,15 +334,78 @@ 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;
erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx);
@@ -354,7 +413,7 @@ check_time_correction(void *unused)
ASSERT(time_sup.inf.c.finalized_offset);
os_mtime = erts_os_monotonic_time();
- sys_gettimeofday(&tod);
+ os_stime = erts_os_system_time();
cdata = time_sup.inf.c.parmon.cdata;
@@ -369,16 +428,13 @@ check_time_correction(void *unused)
time_offset = get_time_offset();
erl_stime = erl_mtime + time_offset;
- os_stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec);
- os_stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec);
-
sdiff = erl_stime - os_stime;
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 +449,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 +470,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 +492,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 +500,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 +531,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 +557,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 +571,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 +633,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 +672,8 @@ check_time_correction(void *unused)
NULL,
NULL,
timeout);
+
+#undef ERTS_PRINT_CORRECTION
}
#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC
@@ -618,9 +682,9 @@ 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;
ddp = &time_sup.inf.c.parmon.cdata.drift;
ix = ddp->ix;
@@ -628,10 +692,7 @@ init_check_time_correction(void *unused)
old_stime = ddp->intervals[0].time.sys;
mtime = erts_os_monotonic_time();
- sys_gettimeofday(&tod);
-
- stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec);
- stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec);
+ stime = erts_os_system_time();
mtime_diff = mtime - old_mtime;
stime_diff = stime - old_stime;
@@ -663,7 +724,7 @@ init_check_time_correction(void *unused)
#endif
static ErtsMonotonicTime
-finalize_corrected_time_offset(SysTimeval *todp)
+finalize_corrected_time_offset(ErtsSystemTime *stimep)
{
ErtsMonotonicTime os_mtime;
ErtsMonotonicCorrectionData cdata;
@@ -672,7 +733,7 @@ finalize_corrected_time_offset(SysTimeval *todp)
erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx);
os_mtime = erts_os_monotonic_time();
- sys_gettimeofday(todp);
+ *stimep = erts_os_system_time();
cdata = time_sup.inf.c.parmon.cdata;
@@ -690,6 +751,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
@@ -707,15 +769,11 @@ late_init_time_correction(void)
static ErtsMonotonicTime get_not_corrected_time(void)
{
- SysTimeval tmp_tv;
ErtsMonotonicTime stime, mtime;
erts_smp_mtx_lock(&erts_get_time_mtx);
- sys_gettimeofday(&tmp_tv);
-
- stime = ERTS_SEC_TO_MONOTONIC(tmp_tv.tv_sec);
- stime += ERTS_USEC_TO_MONOTONIC(tmp_tv.tv_usec);
+ stime = erts_os_system_time();
mtime = stime - time_sup.inf.c.not_corrected_moffset;
@@ -753,7 +811,7 @@ int erts_check_time_adj_support(int time_correction,
/* User wants time correction */
#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
- return !time_sup.r.o.os_monotonic_disable;
+ return !time_sup.r.o.os_monotonic_time_disable;
#else
return 0;
#endif
@@ -782,24 +840,35 @@ void erts_init_sys_time_sup(void)
#endif
#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
- time_sup.r.o.os_monotonic_disable
- = !sys_init_time_res.have_os_monotonic;
- time_sup.r.o.os_monotonic_func
- = sys_init_time_res.os_monotonic_info.func;
- time_sup.r.o.os_monotonic_clock_id
- = sys_init_time_res.os_monotonic_info.clock_id;
- time_sup.r.o.os_monotonic_locked
- = 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_time_disable
+ = !sys_init_time_res.have_os_monotonic_time;
+ time_sup.r.o.os_monotonic_time_func
+ = sys_init_time_res.os_monotonic_time_info.func;
+ time_sup.r.o.os_monotonic_time_clock_id
+ = sys_init_time_res.os_monotonic_time_info.clock_id;
+ time_sup.r.o.os_monotonic_time_locked
+ = sys_init_time_res.os_monotonic_time_info.locked_use;
+ time_sup.r.o.os_monotonic_time_resolution
+ = sys_init_time_res.os_monotonic_time_info.resolution;
+ time_sup.r.o.os_monotonic_time_extended
+ = sys_init_time_res.os_monotonic_time_info.extended;
+ time_sup.r.o.os_system_time_func
+ = sys_init_time_res.os_system_time_info.func;
+ time_sup.r.o.os_system_time_clock_id
+ = sys_init_time_res.os_system_time_info.clock_id;
+ time_sup.r.o.os_system_time_locked
+ = sys_init_time_res.os_system_time_info.locked_use;
+ time_sup.r.o.os_system_time_resolution
+ = sys_init_time_res.os_system_time_info.resolution;
#endif
}
int
erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
{
+ ErtsMonotonicTime resolution;
#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,13 +935,37 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
#endif
+ resolution = time_sup.r.o.os_monotonic_time_resolution;
+ if (resolution > time_sup.r.o.os_system_time_resolution)
+ resolution = time_sup.r.o.os_system_time_resolution;
+
+ 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 /= resolution;
+ if (time_sup.r.o.adj.large_diff < ERTS_USEC_TO_MONOTONIC(500))
+ time_sup.r.o.adj.large_diff = ERTS_USEC_TO_MONOTONIC(500);
+ 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");
#ifndef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
time_sup.r.o.correction = 0;
#else
- if (time_sup.r.o.os_monotonic_disable)
+ if (time_sup.r.o.os_monotonic_time_disable)
time_sup.r.o.correction = 0;
if (time_sup.r.o.correction) {
@@ -874,10 +973,10 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
erts_smp_rwmtx_opt_t rwmtx_opts = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER;
ErtsMonotonicTime offset;
time_sup.inf.c.minit = erts_os_monotonic_time();
- sys_gettimeofday(&time_sup.inf.c.inittv);
+ time_sup.inf.c.sinit = erts_os_system_time();
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 = time_sup.inf.c.sinit;
+ offset -= ERTS_MONOTONIC_TIME_UNIT;
init_time_offset(offset);
rwmtx_opts.type = ERTS_SMP_RWMTX_TYPE_EXTREMELY_FREQUENT_READ;
@@ -889,15 +988,12 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
cdatap = &time_sup.inf.c.parmon.cdata;
#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC
- cdatap->drift.intervals[0].time.sys
- = ERTS_SEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_sec);
- cdatap->drift.intervals[0].time.sys
- += ERTS_USEC_TO_MONOTONIC(time_sup.inf.c.inittv.tv_usec);
+ cdatap->drift.intervals[0].time.sys = time_sup.inf.c.sinit;
cdatap->drift.intervals[0].time.mon = time_sup.inf.c.minit;
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,8 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode)
{
ErtsMonotonicTime stime, offset;
time_sup.r.o.get_time = get_not_corrected_time;
- 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 = time_sup.inf.c.sinit = erts_os_system_time();
+ 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 +1032,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)
@@ -987,17 +1083,12 @@ erts_finalize_time_offset(void)
if (!time_sup.inf.c.finalized_offset) {
ErtsMonotonicTime mtime, new_offset;
- SysTimeval tv;
#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
if (!time_sup.r.o.correction)
#endif
{
- ErtsMonotonicTime stime;
- sys_gettimeofday(&tv);
-
- stime = ERTS_SEC_TO_MONOTONIC(tv.tv_sec);
- stime += ERTS_USEC_TO_MONOTONIC(tv.tv_usec);
+ ErtsMonotonicTime stime = erts_os_system_time();
mtime = stime - time_sup.inf.c.not_corrected_moffset;
@@ -1016,11 +1107,9 @@ erts_finalize_time_offset(void)
}
#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
else {
- mtime = finalize_corrected_time_offset(&tv);
- new_offset = ERTS_SEC_TO_MONOTONIC(tv.tv_sec);
- new_offset += ERTS_USEC_TO_MONOTONIC(tv.tv_usec);
- new_offset -= mtime;
-
+ ErtsSystemTime stime;
+ mtime = finalize_corrected_time_offset(&stime);
+ new_offset = stime - mtime;
}
#endif
new_offset = ERTS_MONOTONIC_TO_USEC(new_offset);
@@ -1515,13 +1604,16 @@ erts_get_monotonic_time(void)
void
get_sys_now(Uint* megasec, Uint* sec, Uint* microsec)
{
- SysTimeval now;
-
- sys_gettimeofday(&now);
-
- *megasec = (Uint) (now.tv_sec / 1000000);
- *sec = (Uint) (now.tv_sec % 1000000);
- *microsec = (Uint) (now.tv_usec);
+ ErtsSystemTime stime = erts_os_system_time();
+ ErtsSystemTime ms, s, us;
+
+ us = ERTS_MONOTONIC_TO_USEC(stime);
+ s = us / (1000*1000);
+ ms = s / (1000*1000);
+
+ *megasec = (Uint) ms;
+ *sec = (Uint) (s - ms*(1000*1000));
+ *microsec = (Uint) (us - s*(1000*1000));
}
#ifdef HAVE_ERTS_NOW_CPU
@@ -1737,7 +1829,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,27 +1839,31 @@ 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)
+ if (time_sup.r.o.os_monotonic_time_disable)
return NIL;
k[i] = erts_bld_atom(hpp, szp, "function");
- v[i++] = erts_bld_atom(hpp, szp, time_sup.r.o.os_monotonic_func);
+ v[i++] = erts_bld_atom(hpp, szp,
+ time_sup.r.o.os_monotonic_time_func);
- if (time_sup.r.o.os_monotonic_clock_id) {
+ if (time_sup.r.o.os_monotonic_time_clock_id) {
k[i] = erts_bld_atom(hpp, szp, "clock_id");
- v[i++] = erts_bld_atom(hpp, szp, time_sup.r.o.os_monotonic_clock_id);
+ v[i++] = erts_bld_atom(hpp, szp,
+ time_sup.r.o.os_monotonic_time_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_time_resolution);
+
+ k[i] = erts_bld_atom(hpp, szp, "extended");
+ v[i++] = time_sup.r.o.os_monotonic_time_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;
+ v[i++] = time_sup.r.o.os_monotonic_time_locked ? am_no : am_yes;
k[i] = erts_bld_atom(hpp, szp, "time");
v[i++] = erts_bld_sint64(hpp, szp, os_mtime);
@@ -1783,7 +1879,7 @@ erts_monotonic_time_source(struct process *c_p)
Eterm *hp = NULL;
Sint64 os_mtime = 0;
#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
- if (!time_sup.r.o.os_monotonic_disable)
+ if (!time_sup.r.o.os_monotonic_time_disable)
os_mtime = (Sint64) erts_os_monotonic_time();
#endif
@@ -1793,6 +1889,49 @@ erts_monotonic_time_source(struct process *c_p)
return bld_monotonic_time_source(&hp, NULL, os_mtime);
}
+static Eterm
+bld_system_time_source(Uint **hpp, Uint *szp, Sint64 os_stime)
+{
+ int i = 0;
+ Eterm k[5];
+ Eterm v[5];
+
+ k[i] = erts_bld_atom(hpp, szp, "function");
+ v[i++] = erts_bld_atom(hpp, szp,
+ time_sup.r.o.os_system_time_func);
+
+ if (time_sup.r.o.os_system_time_clock_id) {
+ k[i] = erts_bld_atom(hpp, szp, "clock_id");
+ v[i++] = erts_bld_atom(hpp, szp,
+ time_sup.r.o.os_system_time_clock_id);
+ }
+
+ k[i] = erts_bld_atom(hpp, szp, "resolution");
+ v[i++] = erts_bld_uint64(hpp, szp,
+ time_sup.r.o.os_system_time_resolution);
+
+ k[i] = erts_bld_atom(hpp, szp, "parallel");
+ v[i++] = am_yes;
+
+ k[i] = erts_bld_atom(hpp, szp, "time");
+ v[i++] = erts_bld_sint64(hpp, szp, os_stime);
+
+ return erts_bld_2tup_list(hpp, szp, (Sint) i, k, v);
+}
+
+Eterm
+erts_system_time_source(struct process *c_p)
+{
+ Uint hsz = 0;
+ Eterm *hp = NULL;
+ Sint64 os_stime = (Sint64) erts_os_system_time();
+
+ bld_system_time_source(NULL, &hsz, os_stime);
+ if (hsz)
+ hp = HAlloc(c_p, hsz);
+ return bld_system_time_source(&hp, NULL, os_stime);
+}
+
#include "bif.h"
@@ -1967,21 +2106,13 @@ BIF_RETTYPE timestamp_0(BIF_ALIST_0)
BIF_RETTYPE os_system_time_0(BIF_ALIST_0)
{
- ErtsMonotonicTime stime;
- SysTimeval tod;
- sys_gettimeofday(&tod);
- stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec);
- stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec);
+ ErtsSystemTime stime = erts_os_system_time();
BIF_RET(make_time_val(BIF_P, stime));
}
BIF_RETTYPE os_system_time_1(BIF_ALIST_0)
{
- ErtsMonotonicTime stime;
- SysTimeval tod;
- sys_gettimeofday(&tod);
- stime = ERTS_SEC_TO_MONOTONIC(tod.tv_sec);
- stime += ERTS_USEC_TO_MONOTONIC(tod.tv_usec);
+ ErtsSystemTime stime = erts_os_system_time();
BIF_RET(time_unit_conversion(BIF_P, BIF_ARG_1, stime, 0));
}
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index a0f35fef1b..251b39508f 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -710,7 +710,7 @@ typedef enum {
} ErtsTimeWarpMode;
typedef struct {
- int have_os_monotonic;
+ int have_os_monotonic_time;
ErtsMonotonicTime os_monotonic_time_unit;
ErtsMonotonicTime sys_clock_resolution;
struct {
@@ -718,7 +718,14 @@ typedef struct {
char *func;
char *clock_id;
int locked_use;
- } os_monotonic_info;
+ int extended;
+ } os_monotonic_time_info;
+ struct {
+ Uint64 resolution;
+ char *func;
+ char *clock_id;
+ int locked_use;
+ } os_system_time_info;
} ErtsSysInitTimeResult;
#define ERTS_SYS_INIT_TIME_RESULT_INITER \
@@ -726,13 +733,13 @@ 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);
extern void erts_sys_init_float(void);
extern void erts_thread_init_float(void);
extern void erts_thread_disable_fpe(void);
-
ERTS_GLB_INLINE int erts_block_fpe(void);
ERTS_GLB_INLINE void erts_unblock_fpe(int);
diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c
index 9f997e1d0b..3dfd3f79d4 100644
--- a/erts/emulator/beam/time.c
+++ b/erts/emulator/beam/time.c
@@ -524,6 +524,7 @@ erts_set_timer(ErlTimer *p, ErlTimeoutProc timeout,
tiw->nto++;
tiw->at_once.nto++;
*tiw->at_once.tail = p;
+ tiw->at_once.tail = &p->next;
p->next = NULL;
p->timeout_pos = timeout_pos;
timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos);
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 46d0b8dc9b..aa158390d6 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -161,12 +161,18 @@ typedef struct tms SysTimes;
#if SIZEOF_LONG == 8
typedef long ErtsMonotonicTime;
+typedef long ErtsSysHrTime;
#elif SIZEOF_LONG_LONG == 8
typedef long long ErtsMonotonicTime;
+typedef long long ErtsSysHrTime;
#else
#error No signed 64-bit type found...
#endif
+typedef ErtsMonotonicTime ErtsSystemTime;
+
+ErtsSystemTime erts_os_system_time(void);
+
#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63)
#define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN)
@@ -201,17 +207,15 @@ ErtsMonotonicTime erts_os_monotonic_time(void);
#elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME)
#define erts_os_monotonic() ((ErtsMonotonicTime) gethrtime())
+#define erts_sys_hrtime() ((ErtsSysHrTime) gethrtime())
#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \
|| 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);
@@ -224,6 +228,15 @@ ErtsMonotonicTime erts_os_monotonic_time(void);
#endif
+/*
+ * erts_sys_hrtime() is the highest resolution
+ * time function found. Time unit is nano-seconds.
+ * It may or may not be monotonic.
+ */
+#ifndef erts_sys_hrtime
+extern ErtsSysHrTime erts_sys_hrtime(void);
+#endif
+
struct erts_sys_time_read_only_data__ {
#ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__
ErtsMonotonicTime (*os_monotonic_time)(void);
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index f1c785890c..7d52650a70 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -556,18 +556,18 @@ erts_sys_pre_init(void)
erts_thr_init(&eid);
-#endif /* USE_THREADS */
-
- erts_init_sys_time_sup();
-
-#ifdef USE_THREADS
-
report_exit_list = NULL;
#ifdef ERTS_ENABLE_LOCK_COUNT
erts_lcnt_init();
#endif
+#endif /* USE_THREADS */
+
+ erts_init_sys_time_sup();
+
+#ifdef USE_THREADS
+
#if CHLDWTHR || defined(ERTS_SMP)
erts_mtx_init(&chld_stat_mtx, "child_status");
#endif
diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c
index 9fdb1930b7..d6591a8296 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
@@ -101,16 +85,23 @@ ErtsSysTimeData__ erts_sys_time_data__ erts_align_attribute(ERTS_CACHE_LINE_SIZE
#define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__
-ErtsMonotonicTime clock_gettime_monotonic_raw(void);
-ErtsMonotonicTime clock_gettime_monotonic_verified(void);
+static ErtsMonotonicTime clock_gettime_monotonic_raw(void);
+static ErtsMonotonicTime clock_gettime_monotonic_verified(void);
#endif /* defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */
#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;
@@ -160,46 +150,46 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
{
#if !defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT)
- init_resp->have_os_monotonic = 0;
+ init_resp->have_os_monotonic_time = 0;
#else /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */
int major, minor, build, vsn;
- init_resp->os_monotonic_info.resolution = (Uint64) 1000*1000*1000;
+ init_resp->os_monotonic_time_info.resolution = (Uint64) 1000*1000*1000;
#if defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID)
{
struct timespec ts;
- if (clock_getres(MONOTONIC_CLOCK_ID, &ts) == 0
- && ts.tv_sec == 0 && ts.tv_nsec != 0) {
- init_resp->os_monotonic_info.resolution /= ts.tv_nsec;
+ if (clock_getres(MONOTONIC_CLOCK_ID, &ts) == 0) {
+ if (ts.tv_sec == 0 && ts.tv_nsec != 0)
+ init_resp->os_monotonic_time_info.resolution /= ts.tv_nsec;
+ else if (ts.tv_sec >= 1)
+ init_resp->os_monotonic_time_info.resolution = 1;
}
}
#endif
#ifdef MONOTONIC_CLOCK_ID_STR
- init_resp->os_monotonic_info.clock_id = MONOTONIC_CLOCK_ID_STR;
+ init_resp->os_monotonic_time_info.clock_id = MONOTONIC_CLOCK_ID_STR;
#else
- init_resp->os_monotonic_info.clock_id = NULL;
+ init_resp->os_monotonic_time_info.clock_id = NULL;
#endif
- init_resp->os_monotonic_info.locked_use = 0;
+ init_resp->os_monotonic_time_info.locked_use = 0;
#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
- init_resp->os_monotonic_info.func = "clock_gettime";
+ init_resp->os_monotonic_time_info.func = "clock_gettime";
#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME)
- init_resp->os_monotonic_info.func = "clock_get_time";
+ init_resp->os_monotonic_time_info.func = "clock_get_time";
#elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME)
- init_resp->os_monotonic_info.func = "gethrtime";
+ init_resp->os_monotonic_time_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();
+ init_resp->os_monotonic_time_info.func = "times";
#else
# error Unknown erts_os_monotonic_time() implementation
#endif
- init_resp->have_os_monotonic = 1;
+ init_resp->have_os_monotonic_time = 1;
os_version(&major, &minor, &build);
@@ -222,7 +212,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
"os_monotonic_time");
internal_state.w.f.last_delivered
= clock_gettime_monotonic_raw();
- init_resp->os_monotonic_info.locked_use = 1;
+ init_resp->os_monotonic_time_info.locked_use = 1;
}
#else /* !(defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)) */
{
@@ -239,7 +229,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF)
if (sysconf(_SC_NPROCESSORS_CONF) > 1)
#endif
- init_resp->have_os_monotonic = 0;
+ init_resp->have_os_monotonic_time = 0;
}
}
}
@@ -247,7 +237,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 +250,171 @@ 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++;
+ }
+
+ init_resp->os_monotonic_time_info.resolution = resolution;
+ init_resp->os_monotonic_time_unit = time_unit;
+ init_resp->os_monotonic_time_info.extended = 1;
+ internal_state.r.o.times_shift = shift;
+
+ 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) */
- internal_state.r.o.ticks_per_sec_wrap
- = (erts_sys_time_data__.r.o.ticks_per_sec
- >> internal_state.r.o.ticks_bsr);
+#ifdef WALL_CLOCK_ID_STR
+ init_resp->os_system_time_info.clock_id = WALL_CLOCK_ID_STR;
+#else
+ init_resp->os_system_time_info.clock_id = NULL;
+#endif
- 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);
+ init_resp->os_system_time_info.locked_use = 0;
+ init_resp->os_system_time_info.resolution = (Uint64) 1000*1000*1000;
+#if defined(HAVE_CLOCK_GETRES) && defined(WALL_CLOCK_ID)
{
- 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;
+ struct timespec ts;
+ if (clock_getres(WALL_CLOCK_ID, &ts) == 0) {
+ if (ts.tv_sec == 0 && ts.tv_nsec != 0)
+ init_resp->os_system_time_info.resolution /= ts.tv_nsec;
+ else if (ts.tv_sec >= 1)
+ init_resp->os_system_time_info.resolution = 1;
+ }
}
+#endif
-#endif /* defined(OS_MONOTONIC_TIME_USING_TIMES) */
+#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME)
+ init_resp->os_system_time_info.func = "clock_gettime";
+#elif defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME)
+ init_resp->os_system_time_info.func = "clock_get_time";
+#elif defined(OS_SYSTEM_TIME_GETTIMEOFDAY)
+ init_resp->os_system_time_info.func = "gettimeofday";
+ init_resp->os_system_time_info.resolution = 1000*1000;
+ init_resp->os_system_time_info.clock_id = NULL;
+#else
+# error Missing erts_os_system_time() implmenentation
+#endif
+
+}
+
+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
+}
+
+static ERTS_INLINE ErtsSystemTime
+adj_stime_time_unit(ErtsSystemTime stime, Uint32 res)
+{
+ if (res == ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT)
+ return stime;
+ if (res == (Uint32) 1000*1000*1000
+ && ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000)
+ return stime/1000;
+ if (res == (Uint32) 1000*1000
+ && ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT == 1000*1000*1000)
+ return stime*1000;
+ return ((ErtsSystemTime)
+ erts_time_unit_conversion(stime,
+ (Uint32) res,
+ (Uint32) ERTS_MONOTONIC_TIME_UNIT));
+}
+
+#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME)
+ErtsSystemTime
+erts_os_system_time(void)
+{
+ ErtsSystemTime stime;
+ struct timespec ts;
+
+ if (clock_gettime(WALL_CLOCK_ID,&ts) != 0) {
+ int err = errno;
+ char *errstr = err ? strerror(err) : "unknown";
+ erl_exit(ERTS_ABORT_EXIT,
+ "clock_gettime(%s, _) failed: %s (%d)\n",
+ WALL_CLOCK_ID_STR, errstr, err);
+
+ }
+
+ stime = (ErtsSystemTime) ts.tv_sec;
+ stime *= (ErtsSystemTime) 1000*1000*1000;
+ stime += (ErtsSystemTime) ts.tv_nsec;
+ return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000);
}
+#elif defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME)
+
+ErtsSystemTime
+erts_os_system_time(void)
+{
+ ErtsSystemTime stime;
+ kern_return_t res;
+ clock_serv_t clk_srv;
+ mach_timespec_t time_spec;
+ int err;
+
+ host_get_clock_service(mach_host_self(),
+ WALL_CLOCK_ID,
+ &clk_srv);
+ errno = 0;
+ res = clock_get_time(clk_srv, &time_spec);
+ err = errno;
+ mach_port_deallocate(mach_task_self(), clk_srv);
+ if (res != KERN_SUCCESS) {
+ char *errstr = err ? strerror(err) : "unknown";
+ erl_exit(ERTS_ABORT_EXIT,
+ "clock_get_time(%s, _) failed: %s (%d)\n",
+ MONOTONIC_CLOCK_ID_STR, errstr, err);
+ }
+
+ stime = (ErtsSystemTime) time_spec.tv_sec;
+ stime *= (ErtsSystemTime) 1000*1000*1000;
+ stime += (ErtsSystemTime) time_spec.tv_nsec;
+
+ return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000);
+}
+
+#elif defined(OS_SYSTEM_TIME_GETTIMEOFDAY)
+
+ErtsSystemTime
+erts_os_system_time(void)
+{
+ ErtsSystemTime stime;
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) != 0) {
+ int err = errno;
+ char *errstr = err ? strerror(err) : "unknown";
+ erl_exit(ERTS_ABORT_EXIT,
+ "gettimeofday(_, NULL) failed: %s (%d)\n",
+ errstr, err);
+ }
+
+ stime = (ErtsSystemTime) tv.tv_sec;
+ stime *= (ErtsSystemTime) 1000*1000;
+ stime += (ErtsSystemTime) tv.tv_usec;
+
+ return adj_stime_time_unit(stime, (Uint32) 1000*1000);
+}
+
+#else
+# error Missing erts_os_system_time() implmenentation
+#endif
+
#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
static ERTS_INLINE ErtsMonotonicTime
@@ -314,7 +439,7 @@ clock_gettime_monotonic(void)
#if defined(__linux__)
-ErtsMonotonicTime clock_gettime_monotonic_verified(void)
+static ErtsMonotonicTime clock_gettime_monotonic_verified(void)
{
ErtsMonotonicTime mtime;
@@ -330,7 +455,7 @@ ErtsMonotonicTime clock_gettime_monotonic_verified(void)
return mtime;
}
-ErtsMonotonicTime clock_gettime_monotonic_raw(void)
+static ErtsMonotonicTime clock_gettime_monotonic_raw(void)
{
return clock_gettime_monotonic();
}
@@ -344,6 +469,12 @@ ErtsMonotonicTime erts_os_monotonic_time(void)
#endif /* !defined(__linux__) */
+ErtsSysHrTime
+erts_sys_hrtime(void)
+{
+ return (ErtsSysHrTime) clock_gettime_monotonic();
+}
+
#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME)
#include <mach/clock.h>
@@ -377,91 +508,47 @@ ErtsMonotonicTime erts_os_monotonic_time(void)
return mtime;
}
-#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)
+ErtsSysHrTime
+erts_sys_hrtime(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);
+ return (ErtsSysHrTime) erts_os_monotonic_time();
}
-#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))
+#elif defined(OS_MONOTONIC_TIME_USING_TIMES)
-/* 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;
- }
- }
+ 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;
+}
- if (internal_state.w.f.last_tick_monotonic_time < tick_monotonic_time)
- internal_state.w.f.last_tick_monotonic_time = tick_monotonic_time;
+# define ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK
- res = internal_state.w.f.last_tick_monotonic_time;
+#else /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */
+/* No os-monotonic-time */
+# define ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK
+#endif
- erts_smp_mtx_unlock(&internal_state.w.f.mtx);
+#ifdef ERTS_NEED_ERTS_SYS_HRTIME_FALLBACK
- return res;
+ErtsSysHrTime
+erts_sys_hrtime(void)
+{
+ ErtsSysHrTime time;
+ struct timeval tv;
+ gettimeofday(&tv);
+ time = (ErtsSysHrTime) tv.tv_sec;
+ time *= (ErtsSysHrTime) 1000*1000*1000;
+ time += ((ErtsSysHrTime) tv.tv_usec)*1000;
+ return time;
}
-#endif /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */
+#endif
+
#ifdef HAVE_GETHRVTIME_PROCFS_IOCTL
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 33aa88ab5f..9aeb460720 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -172,13 +172,19 @@ typedef long long Sint64;
# endif
typedef long long ErtsMonotonicTime;
+typedef long long ErtsSysHrTime;
#else
typedef ULONGLONG Uint64;
typedef LONGLONG Sint64;
typedef LONGLONG ErtsMonotonicTime;
+typedef LONGLONG ErtsSysHrTime;
#endif
+typedef ErtsMonotonicTime ErtsSystemTime;
+
+ErtsSystemTime erts_os_system_time(void);
+
#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63)
#define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN)
@@ -187,6 +193,7 @@ typedef LONGLONG ErtsMonotonicTime;
struct erts_sys_time_read_only_data__ {
ErtsMonotonicTime (*os_monotonic_time)(void);
+ ErtsSysHrTime (*sys_hrtime)(void);
};
typedef struct {
@@ -201,6 +208,7 @@ typedef struct {
extern ErtsSysTimeData__ erts_sys_time_data__;
ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void);
+ERTS_GLB_INLINE ErtsSysHrTime erts_sys_hrtime(void);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
@@ -210,6 +218,12 @@ erts_os_monotonic_time(void)
return (*erts_sys_time_data__.r.o.os_monotonic_time)();
}
+ERTS_GLB_INLINE ErtsSysHrTime
+erts_sys_hrtime(void)
+{
+ return (*erts_sys_time_data__.r.o.sys_hrtime)();
+}
+
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
extern void sys_gettimeofday(SysTimeval *tv);
diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c
index 3a10125c81..da9c4d2e29 100644
--- a/erts/emulator/sys/win32/sys_time.c
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -25,6 +25,8 @@
#endif
#include "sys.h"
#include "assert.h"
+#include "erl_os_monotonic_time_extender.h"
+#include "erl_time.h"
#define LL_LITERAL(X) ERTS_I64_LITERAL(X)
@@ -71,6 +73,8 @@ static int days_in_month[2][13] = {
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}};
+#define ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT 10
+
/*
* erts_os_monotonic_time()
*/
@@ -78,6 +82,12 @@ static int days_in_month[2][13] = {
struct sys_time_internal_state_read_only__ {
ULONGLONG (WINAPI *pGetTickCount64)(void);
BOOL (WINAPI *pQueryPerformanceCounter)(LARGE_INTEGER *);
+ Sint32 pcf;
+ int using_get_tick_count_time_unit;
+};
+
+struct sys_time_internal_state_read_mostly__ {
+ ErtsOsMonotonicTimeExtendState os_mtime_xtnd;
};
struct sys_time_internal_state_write_freq__ {
@@ -94,6 +104,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 +130,63 @@ 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;
+ 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;
+}
- erts_smp_mtx_lock(&internal_state.w.f.mtime_mtx);
+static ErtsMonotonicTime
+os_monotonic_time_gtc64(void)
+{
+ ULONGLONG ticks = (*internal_state.r.o.pGetTickCount64)();
+ return (ErtsMonotonicTime) ticks << 10;
+}
- 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;
+static ErtsSysHrTime
+sys_hrtime_qpc(void)
+{
+ LARGE_INTEGER pc;
- erts_smp_mtx_unlock(&internal_state.w.f.mtime_mtx);
+ if (!(*internal_state.r.o.pQueryPerformanceCounter)(&pc))
+ erl_exit(ERTS_ABORT_EXIT, "QueryPerformanceCounter() failed\n");
- return (ErtsMonotonicTime) res*1000;
+ ASSERT(pc.QuadPart > 0);
+
+ return (ErtsSysHrTime) erts_time_unit_conversion((Uint64) pc.QuadPart,
+ internal_state.r.o.pcf,
+ (Uint32) 1000*1000*1000);
}
-static ErtsMonotonicTime
-os_monotonic_time_gtc64(void)
+static ErtsSysHrTime
+sys_hrtime_gtc32(void)
{
- ULONGLONG ticks = (*internal_state.r.o.pGetTickCount64)();
- return (ErtsMonotonicTime) ticks*1000;
+ ErtsSysHrTime time;
+ Uint32 ticks = (Uint32) GetTickCount();
+ ERTS_CHK_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd,
+ tick_count);
+ time = (ErtsSysHrTime) ERTS_EXTEND_OS_MONOTONIC_TIME(&internal_state.wr.m.os_mtime_xtnd,
+ ticks);
+ time *= (ErtsSysHrTime) (1000 * 1000);
+ return time;
+}
+
+static ErtsSysHrTime
+sys_hrtime_gtc64(void)
+{
+ ErtsSysHrTime time = (*internal_state.r.o.pGetTickCount64)();
+ time *= (ErtsSysHrTime) (1000*1000);
+ return time;
}
/*
@@ -147,11 +197,12 @@ void
sys_init_time(ErtsSysInitTimeResult *init_resp)
{
ErtsMonotonicTime (*os_mtime_func)(void);
+ ErtsSysHrTime (*sys_hrtime_func)(void) = NULL;
ErtsMonotonicTime time_unit;
char kernel_dll_name[] = "kernel32";
HMODULE module;
- init_resp->os_monotonic_info.clock_id = NULL;
+ init_resp->os_monotonic_time_info.clock_id = NULL;
module = GetModuleHandle(kernel_dll_name);
if (!module) {
@@ -161,11 +212,20 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
internal_state.w.f.wrap = 0;
internal_state.w.f.last_tick_count = 0;
- 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;
+ init_resp->os_monotonic_time_info.func = "GetTickCount";
+ init_resp->os_monotonic_time_info.locked_use = 1;
+ /* 10-16 ms resolution according to MicroSoft documentation */
+ init_resp->os_monotonic_time_info.resolution = 100; /* 10 ms */
+ time_unit = (ErtsMonotonicTime) 1000;
+ time_unit <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
+ internal_state.r.o.using_get_tick_count_time_unit = 1;
os_mtime_func = os_monotonic_time_gtc32;
+ init_resp->os_monotonic_time_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 */
+ if (!sys_hrtime_func)
+ sys_hrtime_func = sys_hrtime_gtc32;
}
else {
int major, minor, build;
@@ -182,11 +242,16 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
if (!internal_state.r.o.pGetTickCount64)
goto get_tick_count;
- 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;
+ init_resp->os_monotonic_time_info.func = "GetTickCount64";
+ init_resp->os_monotonic_time_info.locked_use = 0;
+ /* 10-16 ms resolution according to MicroSoft documentation */
+ init_resp->os_monotonic_time_info.resolution = 100; /* 10 ms */
+ time_unit = (ErtsMonotonicTime) 1000;
+ time_unit <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
+ internal_state.r.o.using_get_tick_count_time_unit = 1;
os_mtime_func = os_monotonic_time_gtc64;
+ if (!sys_hrtime_func)
+ sys_hrtime_func = sys_hrtime_gtc64;
}
else { /* Vista or newer... */
@@ -199,40 +264,59 @@ sys_init_time(ErtsSysInitTimeResult *init_resp)
goto get_tick_count64;
if (!(*QPF)(&pf))
goto get_tick_count64;
- /*
- * We only use QueryPerformanceCounter() if
- * its frequency is equal to, or larger than
- * GHz in order to ensure that the user wont
- * be able to observe faulty order between
- * values retrieved on different threads.
- */
- if (pf.QuadPart < (LONGLONG) 1000*1000*1000)
- goto get_tick_count64;
+
internal_state.r.o.pQueryPerformanceCounter
= ((BOOL (WINAPI *)(LARGE_INTEGER *))
GetProcAddress(module, "QueryPerformanceCounter"));
if (!internal_state.r.o.pQueryPerformanceCounter)
goto get_tick_count64;
- init_resp->os_monotonic_info.func = "QueryPerformanceCounter";
- init_resp->os_monotonic_info.locked_use = 0;
+ if (pf.QuadPart < (((LONGLONG) 1) << 32)) {
+ internal_state.r.o.pcf = (Uint32) pf.QuadPart;
+ sys_hrtime_func = sys_hrtime_qpc;
+ }
+
+ /*
+ * We only use QueryPerformanceCounter() for
+ * os-monotonic-time if its frequency is equal
+ * to, or larger than GHz in order to ensure
+ * that the user wont be able to observe faulty
+ * order between values retrieved on different threads.
+ */
+ if (pf.QuadPart < (LONGLONG) 1000*1000*1000)
+ goto get_tick_count64;
+
+ init_resp->os_monotonic_time_info.func = "QueryPerformanceCounter";
+ init_resp->os_monotonic_time_info.locked_use = 0;
time_unit = (ErtsMonotonicTime) pf.QuadPart;
- init_resp->os_monotonic_info.resolution = time_unit;
+ internal_state.r.o.using_get_tick_count_time_unit = 0;
+ init_resp->os_monotonic_time_info.resolution = time_unit;
os_mtime_func = os_monotonic_time_qpc;
}
}
erts_sys_time_data__.r.o.os_monotonic_time = os_mtime_func;
init_resp->os_monotonic_time_unit = time_unit;
- init_resp->have_os_monotonic = 1;
+ init_resp->have_os_monotonic_time = 1;
init_resp->sys_clock_resolution = 1;
+ init_resp->os_system_time_info.func = "GetSystemTime";
+ init_resp->os_system_time_info.clock_id = NULL;
+ init_resp->os_system_time_info.resolution = 100;
+ init_resp->os_system_time_info.locked_use = 0;
+
if(GetTimeZoneInformation(&static_tzi) &&
static_tzi.StandardDate.wMonth != 0 &&
static_tzi.DaylightDate.wMonth != 0) {
have_static_tzi = 1;
}
+}
+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
@@ -513,6 +597,37 @@ sys_gettimeofday(SysTimeval *tv)
EPOCH_JULIAN_DIFF);
}
+ErtsSystemTime
+erts_os_system_time(void)
+{
+ SYSTEMTIME t;
+ FILETIME ft;
+ ULARGE_INTEGER ull;
+ ErtsSystemTime stime;
+
+ GetSystemTime(&t);
+ SystemTimeToFileTime(&t, &ft);
+ FILETIME_TO_ULI(ull,ft);
+
+ /* now in 100 ns units */
+
+ stime = (ErtsSystemTime) ull.QuadPart;
+ stime -= (((ErtsSystemTime) EPOCH_JULIAN_DIFF)
+ * ((ErtsSystemTime) (10*1000*1000)));
+ stime /= (ErtsSystemTime) (10*1000);
+
+ if (internal_state.r.o.using_get_tick_count_time_unit) {
+ stime <<= ERTS_GET_TICK_COUNT_TIME_UNIT_SHIFT;
+ return stime;
+ }
+
+ return ((ErtsSystemTime)
+ erts_time_unit_conversion(stime,
+ (Uint32) 1000,
+ (Uint32) ERTS_MONOTONIC_TIME_UNIT));
+}
+
+
clock_t
sys_times(SysTimes *buffer) {
clock_t kernel_ticks = (GetTickCount() /