aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/unix/sys_time.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys/unix/sys_time.c')
-rw-r--r--erts/emulator/sys/unix/sys_time.c391
1 files changed, 239 insertions, 152 deletions
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