aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys')
-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
7 files changed, 580 insertions, 198 deletions
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() /