[erlang-patches] Time correction with clock_gettime() (Was: Re: More about SCTP and R12B-5)

Per Hedeland <>
Sat Nov 8 14:24:41 CET 2008


Raimo Niskanen <> wrote:
>
>On Thu, Nov 06, 2008 at 03:54:40PM +0100, Per Hedeland wrote:
>> 
>> Which reminds me, I recently fixed the Erlang "virtual time" to work on
>> *BSD (and QNX!:-), using clock_gettime(). However that was for R10, and
>> when I looked at this in R12 (which I had already done, but apparently
>> in the wrong place) in preparation for sending in a patch, I found that
>> it had already been done - but only for Linux, and in a way that I don't
>> think will work in general. I.e. it seemed to assume that
>> clock_gettime(CLOCK_MONOTONIC) has anything near the actual resolution
>> possible with the struct timespec that it returns - that is definitely
>> not true on *BSD or QNX, did you verify it on Linux?
>
>I do not know about this one, but can you re-submit your R10 patch,
>or let us know how it should be done in R12?

There's no re- since I never submitted it, due to stumbling on the
conflicting change in R12 (the conflict is probably "only" in the
configure test though). After a bit more testing of clock_gettime()
behaviour, I believe that the R12 way (i.e. using clock_gettime() as
replacement for the Solaris-specific gethrtime()) is fine on Linux 2.6
(only place R12 uses it) and FreeBSD 6.x/7.x, where CLOCK_MONOTONIC does
seem to have at least 1 usec resolution, but not on NetBSD (10 msec) or
QNX (1-2 msec) (I have no OpenBSD to test on). Sigh, it's a mess...

Anyway I enclose my R10 patch FYI - I certainly don't expect you to do
anything with it. It uses the more pessimistic approach of
clock_gettime() as replacement for times(), which I believe should work
on all systems that pass the configure test, but I guess the result is
"better" if you can use it as replacement for gethrtime(). If you would
be interested in a "merge" to R12, using this approach for the
non-Linux/Solaris case only, I'll try to find the time to create one (I
will have to at some point anyway...).

And for those that think this is just of academic interest, I'll only
say that neither "Use NTP" nor "Use Linux" are viable answers when your
customer, that is using your product in his (e.g.) BSD-based product
(and has his own customers in turn), complains "I get thrown out of the
CLI with idle-timeout when I set the time forward".:-)

--Per

Index: erts/emulator/beam/erl_time_sup.c
===================================================================
--- erts/emulator/beam/erl_time_sup.c	(revision 19340)
+++ erts/emulator/beam/erl_time_sup.c	(revision 19523)
@@ -176,7 +176,7 @@
 #define correction (hr_correction/1000000)
 
 #else /* !HAVE_GETHRTIME */
-#if !defined(CORRECT_USING_TIMES) 
+#if !defined(CORRECT_USING_TIMES) && !defined(CORRECT_USING_CLOCK_GETTIME)
 #define init_tolerant_timeofday() 
 #define get_tolerant_timeofday(tvp) sys_gettimeofday(tvp)
 #else
@@ -191,6 +191,8 @@
 static Milli last_cc; 
 static clock_t last_ct;
 
+#if defined(CORRECT_USING_TIMES)
+
 /* sys_times() might need to be wrapped and the values shifted (right)
    a bit to cope with newer linux (2.5.*) kernels, this has to be taken care 
    of dynamically to start with, a special version that uses
@@ -208,6 +210,28 @@
 
 #endif
 
+#else  /* CORRECT_USING_CLOCK_GETTIME */
+
+static clock_t clock_gettime_ticks()
+{
+    struct timespec ts;
+
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+#ifdef ERTS_WRAP_SYS_TIMES
+    return ((clock_t)(ts.tv_sec * SYS_CLK_TCK_WRAP +
+                      ts.tv_nsec / (1000000000 / SYS_CLK_TCK_WRAP))) &
+        ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1);
+#else
+    return ((clock_t)(ts.tv_sec * SYS_CLK_TCK +
+                      ts.tv_nsec / (1000000000 / SYS_CLK_TCK))) &
+        ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1);
+#endif
+}
+
+#define KERNEL_TICKS() clock_gettime_ticks()
+
+#endif
+
 static void init_tolerant_timeofday(void)
 {
     last_ct = init_ct = KERNEL_TICKS();
@@ -249,7 +273,12 @@
 	ct_wrap += ((Sint64) 1) << ((sizeof(clock_t) * 8) - 1);
     }
     last_ct = current_ct;
-    ct_diff = ((ct_wrap + current_ct) - init_ct) * TICK_MS;
+    /* Can't use TICK_MS here unless ticks is an integer fraction of 1000 */
+#ifdef ERTS_WRAP_SYS_TIMES
+    ct_diff = ((ct_wrap + current_ct) - init_ct) * 1000 / SYS_CLK_TCK_WRAP;
+#else
+    ct_diff = ((ct_wrap + current_ct) - init_ct) * 1000 / SYS_CLK_TCK;
+#endif
 
     /*
      * We will adjust the time in milliseconds and we allow for 1% 
@@ -326,7 +355,7 @@
 #undef TICK_MS
 }
 
-#endif /* CORRECT_USING_TIMES */
+#endif /* CORRECT_USING_TIMES || CORRECT_USING_CLOCK_GETTIME */
 #endif /* !HAVE_GETHRTIME */
 
 /*
Index: erts/acconfig.h
===================================================================
--- erts/acconfig.h	(revision 19340)
+++ erts/acconfig.h	(revision 19523)
@@ -18,6 +18,9 @@
 /* Define if you do not have a high-res. timer & want to use times() instead */
 #undef CORRECT_USING_TIMES
 
+/* Define if you do not have gethrtime() and want
+   to use clock_gettime(CLOCK_MONOTONIC) instead */
+#undef CORRECT_USING_CLOCK_GETTIME
 
 /*
  * HiPE enable or not.
Index: erts/aclocal.m4
===================================================================
--- erts/aclocal.m4	(revision 19340)
+++ erts/aclocal.m4	(revision 19523)
@@ -625,7 +625,10 @@
 dnl is implemented using gettimeofday so it doesn't make much sense to
 dnl use it there...) On second thought, it seems to be safer to do it the
 dnl other way around. I.e. only use times() on OS's where we know it will
-dnl work...
+dnl work... Update: Actually times() works right on FreeBSD 6.0 and later,
+dnl but still not on NetBSD/OpenBSD - and a "better" fallback is to use
+dnl clock_gettime(CLOCK_MONOTONIC) anyway - more deterministic and here we
+dnl don't care about the accounting stuff that times() collects.
 dnl
 
 AC_DEFUN(ERL_TIME_CORRECTION,
@@ -640,8 +643,26 @@
     case $host_os in
       linux*)
         erl_cv_time_correction=times ;;
+      *qnx*)
+        # clock_gettime(CLOCK_MONOTONIC) works on QNX 6 at least
+        # (can't run test below when cross-compiling)
+        erl_cv_time_correction=clock_gettime ;;
       *)
-        erl_cv_time_correction=none ;;
+	AC_TRY_RUN([
+	#include <stdlib.h>
+	#include <unistd.h>
+	#include <string.h>
+	#include <stdio.h>
+	#include <time.h>
+	int main() {
+	    struct timespec tp;
+
+	    if (clock_gettime(CLOCK_MONOTONIC, &tp) < 0)
+	      exit(1);
+	    exit(0); return 0;
+	  }
+	], erl_cv_time_correction=clock_gettime, erl_cv_time_correction=none, erl_cv_time_correction=none)
+        ;;
     esac
     ;;
 esac
@@ -650,6 +671,9 @@
   times)
     AC_DEFINE(CORRECT_USING_TIMES)
     ;;
+  clock_gettime)
+    AC_DEFINE(CORRECT_USING_CLOCK_GETTIME)
+    ;;
 esac
 ])dnl
 



More information about the erlang-patches mailing list