aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/unix
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys/unix')
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h120
-rw-r--r--erts/emulator/sys/unix/sys.c24
-rw-r--r--erts/emulator/sys/unix/sys_time.c398
3 files changed, 483 insertions, 59 deletions
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index 26ed2fb558..5417bb2687 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -114,11 +114,6 @@
/*
* Make sure that MAXPATHLEN is defined.
*/
-#ifdef GETHRTIME_WITH_CLOCK_GETTIME
-#undef HAVE_GETHRTIME
-#define HAVE_GETHRTIME 1
-#endif
-
#ifndef MAXPATHLEN
# ifdef PATH_MAX
# define MAXPATHLEN PATH_MAX
@@ -160,33 +155,112 @@ typedef struct timeval SysTimeval;
typedef struct tms SysTimes;
-extern int erts_ticks_per_sec;
-
-#define SYS_CLK_TCK (erts_ticks_per_sec)
+#define SYS_CLK_TCK (erts_sys_time_data__.r.o.ticks_per_sec)
#define sys_times(Arg) times(Arg)
-#define ERTS_WRAP_SYS_TIMES 1
-extern int erts_ticks_per_sec_wrap;
-#define SYS_CLK_TCK_WRAP (erts_ticks_per_sec_wrap)
-extern clock_t sys_times_wrap(void);
+#if SIZEOF_LONG == 8
+typedef long ErtsMonotonicTime;
+#elif SIZEOF_LONG_LONG == 8
+typedef long long ErtsMonotonicTime;
+#else
+#error No signed 64-bit type found...
+#endif
+
+#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63)
+#define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN)
+
+/*
+ * OS monotonic time
+ */
+
+/*
+ * Most common with os monotonic time using nano second
+ * time unit. These defines are modified below if this
+ * isn't the case...
+ */
+#define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1
+#define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000*1000)
+
+#undef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__
+#undef ERTS_HAVE_CORRECTED_OS_MONOTONIC
+
+#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
+
+#if defined(__linux__)
-#ifdef HAVE_GETHRTIME
-#ifdef GETHRTIME_WITH_CLOCK_GETTIME
-typedef long long SysHrTime;
+#define ERTS_HAVE_CORRECTED_OS_MONOTONIC 1
+#define ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ 1
-extern SysHrTime sys_gethrtime(void);
-#define sys_init_hrtime() /* Nothing */
+#else /* !defined(__linux__) */
-#else /* Real gethrtime (Solaris) */
+ErtsMonotonicTime erts_os_monotonic_time(void);
-typedef hrtime_t SysHrTime;
+#endif /* !defined(__linux__) */
+
+#elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME)
+
+#define erts_os_monotonic() ((ErtsMonotonicTime) gethrtime())
+
+#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \
+ || defined(OS_MONOTONIC_TIME_USING_TIMES)
+
+#if defined(OS_MONOTONIC_TIME_USING_TIMES)
+# 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);
+#endif
-#define sys_gethrtime() gethrtime()
-#define sys_init_hrtime() /* Nothing */
+ErtsMonotonicTime erts_os_monotonic_time(void);
-#endif /* GETHRTIME_WITH_CLOCK_GETTIME */
-#endif /* HAVE_GETHRTIME */
+#else /* No OS monotonic available... */
+
+#undef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT
+#undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT
+#define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000)
+
+#endif
+
+struct erts_sys_time_read_only_data__ {
+#ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__
+ ErtsMonotonicTime (*os_monotonic_time)(void);
+#endif
+ int ticks_per_sec;
+};
+
+typedef struct {
+ union {
+ struct erts_sys_time_read_only_data__ o;
+ char align__[(((sizeof(struct erts_sys_time_read_only_data__) - 1)
+ / ASSUMED_CACHE_LINE_SIZE) + 1)
+ * ASSUMED_CACHE_LINE_SIZE];
+ } r;
+} ErtsSysTimeData__;
+
+extern ErtsSysTimeData__ erts_sys_time_data__;
+
+#ifdef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__
+
+ERTS_GLB_INLINE ErtsMonotonicTime erts_os_monotonic_time(void);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE ErtsMonotonicTime
+erts_os_monotonic_time(void)
+{
+ return (*erts_sys_time_data__.r.o.os_monotonic_time)();
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ */
+
+/*
+ *
+ */
#if (defined(HAVE_GETHRVTIME) || defined(HAVE_CLOCK_GETTIME))
typedef long long SysCpuTime;
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 0d677d5f34..70f549a37a 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -280,7 +280,7 @@ struct {
int (*event)(ErlDrvPort, ErlDrvEvent, ErlDrvEventData);
void (*check_io_as_interrupt)(void);
void (*check_io_interrupt)(int);
- void (*check_io_interrupt_tmd)(int, erts_short_time_t);
+ void (*check_io_interrupt_tmd)(int, ErtsMonotonicTime);
void (*check_io)(int);
Uint (*size)(void);
Eterm (*info)(void *);
@@ -386,9 +386,9 @@ erts_sys_schedule_interrupt(int set)
#ifdef ERTS_SMP
void
-erts_sys_schedule_interrupt_timed(int set, erts_short_time_t msec)
+erts_sys_schedule_interrupt_timed(int set, ErtsMonotonicTime timeout_time)
{
- ERTS_CHK_IO_INTR_TMD(set, msec);
+ ERTS_CHK_IO_INTR_TMD(set, timeout_time);
}
#endif
@@ -984,24 +984,6 @@ static void unblock_signals(void)
#endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
#endif
}
-/************************** Time stuff **************************/
-#ifdef HAVE_GETHRTIME
-#ifdef GETHRTIME_WITH_CLOCK_GETTIME
-
-SysHrTime sys_gethrtime(void)
-{
- struct timespec ts;
- long long result;
- if (clock_gettime(CLOCK_MONOTONIC,&ts) != 0) {
- erl_exit(1,"Fatal, could not get clock_monotonic value!, "
- "errno = %d\n", errno);
- }
- result = ((long long) ts.tv_sec) * 1000000000LL +
- ((long long) ts.tv_nsec);
- return (SysHrTime) result;
-}
-#endif
-#endif
/************************** OS info *******************************/
diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c
index fcce54a2c4..9fdb1930b7 100644
--- a/erts/emulator/sys/unix/sys_time.c
+++ b/erts/emulator/sys/unix/sys_time.c
@@ -53,9 +53,40 @@
/******************* Routines for time measurement *********************/
-int erts_ticks_per_sec = 0; /* Will be SYS_CLK_TCK in erl_unix_sys.h */
-int erts_ticks_per_sec_wrap = 0; /* Will be SYS_CLK_TCK_WRAP */
-static int ticks_bsr = 0; /* Shift wrapped tick value this much to the right */
+#undef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__
+#undef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__
+
+#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
+
+#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
+
+#endif
/*
* init timers, chose a tick length, and return it.
@@ -63,37 +94,374 @@ static int ticks_bsr = 0; /* Shift wrapped tick value this much to the right */
* does almost everything. Other platforms have to
* emulate Unix in this sense.
*/
-int sys_init_time(void)
+
+ErtsSysTimeData__ erts_sys_time_data__ erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+
+#if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
+
+#define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__
+
+ErtsMonotonicTime clock_gettime_monotonic_raw(void);
+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;
+#endif
+};
+#endif
+
+#ifdef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__
+struct sys_time_internal_state_write_freq__ {
+ erts_smp_mtx_t mtx;
+#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
+
+#if defined(ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__) \
+ || defined(ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__)
+static struct {
+#ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__
+ union {
+ struct sys_time_internal_state_read_only__ o;
+ char align__[(((sizeof(struct sys_time_internal_state_read_only__) - 1)
+ / ASSUMED_CACHE_LINE_SIZE) + 1)
+ * ASSUMED_CACHE_LINE_SIZE];
+ } r;
+#endif
+#ifdef ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__
+ 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)
+ * ASSUMED_CACHE_LINE_SIZE];
+ } w;
+#endif
+} internal_state erts_align_attribute(ERTS_CACHE_LINE_SIZE);
+#endif
+
+void
+sys_init_time(ErtsSysInitTimeResult *init_resp)
{
+#if !defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT)
+
+ init_resp->have_os_monotonic = 0;
+
+#else /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */
+
+ int major, minor, build, vsn;
+
+ init_resp->os_monotonic_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;
+ }
+ }
+#endif
+
+#ifdef MONOTONIC_CLOCK_ID_STR
+ init_resp->os_monotonic_info.clock_id = MONOTONIC_CLOCK_ID_STR;
+#else
+ init_resp->os_monotonic_info.clock_id = NULL;
+#endif
+
+ init_resp->os_monotonic_info.locked_use = 0;
+
+#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
+ init_resp->os_monotonic_info.func = "clock_gettime";
+#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME)
+ init_resp->os_monotonic_info.func = "clock_get_time";
+#elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME)
+ 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
+
+ init_resp->have_os_monotonic = 1;
+
+ os_version(&major, &minor, &build);
+
+ vsn = ERTS_MK_VSN_INT(major, minor, build);
+
+
+#if defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
+ if (vsn >= ERTS_MK_VSN_INT(2, 6, 33))
+ erts_sys_time_data__.r.o.os_monotonic_time =
+ clock_gettime_monotonic_raw;
+ else {
+ /*
+ * Linux versions prior to 2.6.33 have a
+ * known bug that sometimes cause monotonic
+ * time to take small steps backwards.
+ */
+ erts_sys_time_data__.r.o.os_monotonic_time =
+ clock_gettime_monotonic_verified;
+ erts_smp_mtx_init(&internal_state.w.f.mtx,
+ "os_monotonic_time");
+ internal_state.w.f.last_delivered
+ = clock_gettime_monotonic_raw();
+ init_resp->os_monotonic_info.locked_use = 1;
+ }
+#else /* !(defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)) */
+ {
+ char flavor[1024];
+
+ os_flavor(flavor, sizeof(flavor));
+
+ if (sys_strcmp(flavor, "sunos") == 0) {
+ /*
+ * Don't trust hrtime on multi processors
+ * on SunOS prior to SunOS 5.8
+ */
+ if (vsn < ERTS_MK_VSN_INT(5, 8, 0)) {
+#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF)
+ if (sysconf(_SC_NPROCESSORS_CONF) > 1)
+#endif
+ init_resp->have_os_monotonic = 0;
+ }
+ }
+ }
+#endif /* !(defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)) */
+
+#endif /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */
+
+ init_resp->os_monotonic_time_unit = ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT;
+ init_resp->sys_clock_resolution = SYS_CLOCK_RESOLUTION;
+
/*
- * This (erts_ticks_per_sec) is only for times() (CLK_TCK),
- * the resolution is always one millisecond..
+ * This (erts_sys_time_data__.r.o.ticks_per_sec) is only for
+ * times() (CLK_TCK), the resolution is always one millisecond..
*/
- if ((erts_ticks_per_sec = TICKS_PER_SEC()) < 0)
- erl_exit(1, "Can't get clock ticks/sec\n");
- if (erts_ticks_per_sec >= 1000) {
+ if ((erts_sys_time_data__.r.o.ticks_per_sec = TICKS_PER_SEC()) < 0)
+ erl_exit(ERTS_ABORT_EXIT, "Can't get clock ticks/sec\n");
+
+#if defined(OS_MONOTONIC_TIME_USING_TIMES)
+
+ 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 */
- ticks_bsr = 3;
+ internal_state.r.o.ticks_bsr = 3;
} else {
- ticks_bsr = 0;
+ internal_state.r.o.ticks_bsr = 0;
}
- erts_ticks_per_sec_wrap = (erts_ticks_per_sec >> ticks_bsr);
- return SYS_CLOCK_RESOLUTION;
+
+ internal_state.r.o.ticks_per_sec_wrap
+ = (erts_sys_time_data__.r.o.ticks_per_sec
+ >> internal_state.r.o.ticks_bsr);
+
+ 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;
+ }
+
+#endif /* defined(OS_MONOTONIC_TIME_USING_TIMES) */
+
+}
+
+#if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
+
+static ERTS_INLINE ErtsMonotonicTime
+clock_gettime_monotonic(void)
+{
+ ErtsMonotonicTime mtime;
+ struct timespec ts;
+
+ if (clock_gettime(MONOTONIC_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",
+ MONOTONIC_CLOCK_ID_STR, errstr, err);
+
+ }
+ mtime = (ErtsMonotonicTime) ts.tv_sec;
+ mtime *= (ErtsMonotonicTime) 1000*1000*1000;
+ mtime += (ErtsMonotonicTime) ts.tv_nsec;
+ return mtime;
+}
+
+#if defined(__linux__)
+
+ErtsMonotonicTime clock_gettime_monotonic_verified(void)
+{
+ ErtsMonotonicTime mtime;
+
+ mtime = clock_gettime_monotonic();
+
+ erts_smp_mtx_lock(&internal_state.w.f.mtx);
+ if (mtime < internal_state.w.f.last_delivered)
+ mtime = internal_state.w.f.last_delivered;
+ else
+ internal_state.w.f.last_delivered = mtime;
+ erts_smp_mtx_unlock(&internal_state.w.f.mtx);
+
+ return mtime;
+}
+
+ErtsMonotonicTime clock_gettime_monotonic_raw(void)
+{
+ return clock_gettime_monotonic();
}
-clock_t sys_times_wrap(void)
+#else /* !defined(__linux__) */
+
+ErtsMonotonicTime erts_os_monotonic_time(void)
+{
+ return clock_gettime_monotonic();
+}
+
+#endif /* !defined(__linux__) */
+
+#elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME)
+
+#include <mach/clock.h>
+#include <mach/mach.h>
+
+ErtsMonotonicTime erts_os_monotonic_time(void)
+{
+ ErtsMonotonicTime mtime;
+ kern_return_t res;
+ clock_serv_t clk_srv;
+ mach_timespec_t time_spec;
+ int err;
+
+ host_get_clock_service(mach_host_self(),
+ MONOTONIC_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);
+ }
+
+ mtime = (ErtsMonotonicTime) time_spec.tv_sec;
+ mtime *= (ErtsMonotonicTime) 1000*1000*1000;
+ mtime += (ErtsMonotonicTime) time_spec.tv_nsec;
+ return mtime;
+}
+
+#elif defined(OS_MONOTONIC_TIME_USING_TIMES)
+
+static clock_t sys_times_wrap(void)
{
SysTimes dummy;
- clock_t result = (sys_times(&dummy) >> ticks_bsr);
+ 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;
+}
+#endif /* !defined(OS_MONOTONIC_TIME_USING_TIMES) */
#ifdef HAVE_GETHRVTIME_PROCFS_IOCTL