diff options
author | Rickard Green <[email protected]> | 2015-03-26 22:46:29 +0100 |
---|---|---|
committer | Rickard Green <[email protected]> | 2015-05-06 15:58:32 +0200 |
commit | edce22eb43c40359ccbd0924412cf13692744779 (patch) | |
tree | 4ecb3fd82be20f5e1bfa49e777310ca04c2d8ab8 /erts | |
parent | bf3b377220f2531b9b101f32222067beb3ea750b (diff) | |
download | otp-edce22eb43c40359ccbd0924412cf13692744779.tar.gz otp-edce22eb43c40359ccbd0924412cf13692744779.tar.bz2 otp-edce22eb43c40359ccbd0924412cf13692744779.zip |
Misc time improvements
- Possibility to chose different clock sources
- Improved mach clock usage
- Improved linux clock_gettime() usage
- ...
Diffstat (limited to 'erts')
-rw-r--r-- | erts/aclocal.m4 | 306 | ||||
-rw-r--r-- | erts/configure.in | 9 | ||||
-rw-r--r-- | erts/doc/src/time_correction.xml | 87 | ||||
-rw-r--r-- | erts/emulator/beam/erl_gc.c | 21 | ||||
-rw-r--r-- | erts/emulator/beam/erl_time.h | 9 | ||||
-rw-r--r-- | erts/emulator/beam/erl_time_sup.c | 507 | ||||
-rw-r--r-- | erts/emulator/beam/sys.h | 10 | ||||
-rw-r--r-- | erts/emulator/beam/time.c | 66 | ||||
-rw-r--r-- | erts/emulator/sys/unix/erl_unix_sys.h | 6 | ||||
-rw-r--r-- | erts/emulator/sys/unix/sys_time.c | 433 | ||||
-rw-r--r-- | erts/emulator/sys/win32/erl_poll.c | 1 | ||||
-rw-r--r-- | erts/emulator/sys/win32/erl_win_sys.h | 2 | ||||
-rw-r--r-- | erts/emulator/sys/win32/sys_time.c | 16 |
13 files changed, 988 insertions, 485 deletions
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 83f735d332..abc55967ec 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -60,7 +60,6 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us dnl Cross compilation variables AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)]) -AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigaltstack, [have working sigaltstack(): yes|no (only used when cross compiling)]) @@ -726,9 +725,48 @@ esac AC_DEFUN(ERL_MONOTONIC_CLOCK, [ - AC_CACHE_CHECK([for clock_gettime() with monotonic clock type], erl_cv_clock_gettime_monotonic, + default_resolution_clock_gettime_monotonic="CLOCK_HIGHRES CLOCK_BOOTTIME CLOCK_MONOTONIC" + low_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST" + high_resolution_clock_gettime_monotonic="CLOCK_MONOTONIC_PRECISE" + + case "$1" in + high_resolution) + check_msg="high resolution " + prefer_resolution_clock_gettime_monotonic="$high_resolution_clock_gettime_monotonic" + ;; + low_resolution) + check_msg="low resolution " + prefer_resolution_clock_gettime_monotonic="$low_resolution_clock_gettime_monotonic" + ;; + custom_resolution) + check_msg="custom resolution " + prefer_resolution_clock_gettime_monotonic="$2" + ;; + *) + check_msg="" + prefer_resolution_clock_gettime_monotonic= + ;; + esac + + AC_CACHE_CHECK([for clock_gettime(CLOCK_MONOTONIC_RAW, _)], erl_cv_clock_gettime_monotonic_raw, [ - for clock_type in CLOCK_HIGHRES CLOCK_MONOTONIC CLOCK_MONOTONIC_PRECISE; do + AC_TRY_COMPILE([ +#include <time.h> + ], + [ + struct timespec ts; + long long result; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + result = ((long long) ts.tv_sec) * 1000000000LL + + ((long long) ts.tv_nsec); + ], + erl_cv_clock_gettime_monotonic_raw=yes, + erl_cv_clock_gettime_monotonic_raw=no) + ]) + + AC_CACHE_CHECK([for clock_gettime() with ${check_msg}monotonic clock type], erl_cv_clock_gettime_monotonic_$1, + [ + for clock_type in $prefer_resolution_clock_gettime_monotonic $default_resolution_clock_gettime_monotonic $high_resolution_clock_gettime_monotonic $low_resolution_clock_gettime_monotonic; do AC_TRY_COMPILE([ #include <time.h> ], @@ -739,12 +777,12 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, result = ((long long) ts.tv_sec) * 1000000000LL + ((long long) ts.tv_nsec); ], - erl_cv_clock_gettime_monotonic=$clock_type, - erl_cv_clock_gettime_monotonic=no) - test $erl_cv_clock_gettime_monotonic = no || break + erl_cv_clock_gettime_monotonic_$1=$clock_type, + erl_cv_clock_gettime_monotonic_$1=no) + test $erl_cv_clock_gettime_monotonic_$1 = no || break done ]) - + AC_CHECK_FUNCS([clock_getres clock_get_attributes gethrtime]) AC_CACHE_CHECK([for mach clock_get_time() with monotonic clock type], erl_cv_mach_clock_get_time_monotonic, @@ -766,39 +804,24 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, erl_cv_mach_clock_get_time_monotonic=no) ]) - case $erl_cv_clock_gettime_monotonic-$ac_cv_func_gethrtime-$erl_cv_mach_clock_get_time_monotonic-$host_os in + erl_corrected_monotonic_clock=no + case $erl_cv_clock_gettime_monotonic_$1-$ac_cv_func_gethrtime-$erl_cv_mach_clock_get_time_monotonic-$host_os in *-*-*-win32) erl_monotonic_clock_func=WindowsAPI ;; CLOCK_*-*-*-linux*) - if test X$cross_compiling != Xyes; then - linux_kernel_vsn_=`uname -r` - case $linux_kernel_vsn_ in - [[0-1]].*|2.[[0-5]]|2.[[0-5]].*) - erl_monotonic_clock_func=times - ;; - *) - erl_monotonic_clock_func=clock_gettime - ;; - esac - else - case X$erl_xcomp_linux_clock_gettime_correction in - X) - AC_MSG_WARN([result clock_gettime guessed because of cross compilation]) - erl_monotonic_clock_func=clock_gettime - ;; - Xyes|Xno) - if test $erl_xcomp_linux_clock_gettime_correction = yes; then - erl_monotonic_clock_func=clock_gettime - else - erl_monotonic_clock_func=times - fi - ;; - *) - AC_MSG_ERROR([Bad erl_xcomp_linux_clock_gettime_correction value: $erl_xcomp_linux_clock_gettime_correction]) - ;; - esac - fi + case $erl_cv_clock_gettime_monotonic_$1-$erl_cv_clock_gettime_monotonic_raw in + CLOCK_BOOTTIME-yes|CLOCK_MONOTONIC-yes) + erl_corrected_monotonic_clock=yes + ;; + *) + # We don't trust CLOCK_MONOTONIC to be NTP + # adjusted on linux systems that do not have + # CLOCK_MONOTONIC_RAW (although it seems to + # be...) + ;; + esac + erl_monotonic_clock_func=clock_gettime ;; no-no-no-linux*) erl_monotonic_clock_func=times @@ -817,16 +840,26 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, ;; esac + erl_monotonic_clock_low_resolution=no erl_monotonic_clock_lib= erl_monotonic_clock_id= case $erl_monotonic_clock_func in clock_gettime) - erl_monotonic_clock_id="$erl_cv_clock_gettime_monotonic" + erl_monotonic_clock_id=$erl_cv_clock_gettime_monotonic_$1 + for low_res_id in $low_resolution_clock_gettime_monotonic; do + if test $erl_monotonic_clock_id = $low_res_id; then + erl_monotonic_clock_low_resolution=yes + break + fi + done AC_CHECK_LIB(rt, clock_gettime, [erl_monotonic_clock_lib="-lrt"]) ;; mach_clock_get_time) erl_monotonic_clock_id=SYSTEM_CLOCK ;; + times) + erl_monotonic_clock_low_resolution=yes + ;; *) ;; esac @@ -835,9 +868,32 @@ AC_DEFUN(ERL_MONOTONIC_CLOCK, AC_DEFUN(ERL_WALL_CLOCK, [ - AC_CACHE_CHECK([for clock_gettime() with wall clock type], erl_cv_clock_gettime_wall, + default_resolution_clock_gettime_wall="CLOCK_REALTIME" + low_resolution_clock_gettime_wall="CLOCK_REALTIME_COARSE CLOCK_REALTIME_FAST" + high_resolution_clock_gettime_wall="CLOCK_REALTIME_PRECISE" + + case "$1" in + high_resolution) + check_msg="high resolution " + prefer_resolution_clock_gettime_wall="$high_resolution_clock_gettime_wall" + ;; + low_resolution) + check_msg="low resolution " + prefer_resolution_clock_gettime_wall="$low_resolution_clock_gettime_wall" + ;; + custom_resolution) + check_msg="custom resolution " + prefer_resolution_clock_gettime_wall="$2" + ;; + *) + check_msg="" + prefer_resolution_clock_gettime_wall= + ;; + esac + + AC_CACHE_CHECK([for clock_gettime() with ${check_msg}wall clock type], erl_cv_clock_gettime_wall_$1, [ - for clock_type in CLOCK_REALTIME; do + for clock_type in $prefer_resolution_clock_gettime_wall $default_resolution_clock_gettime_wall $high_resolution_clock_gettime_wall $low_resolution_clock_gettime_wall; do AC_TRY_COMPILE([ #include <time.h> ], @@ -848,12 +904,12 @@ AC_DEFUN(ERL_WALL_CLOCK, result = ((long long) ts.tv_sec) * 1000000000LL + ((long long) ts.tv_nsec); ], - erl_cv_clock_gettime_wall=$clock_type, - erl_cv_clock_gettime_wall=no) - test $erl_cv_clock_gettime_wall = no || break + erl_cv_clock_gettime_wall_$1=$clock_type, + erl_cv_clock_gettime_wall_$1=no) + test $erl_cv_clock_gettime_wall_$1 = no || break done ]) - + AC_CHECK_FUNCS([clock_getres clock_get_attributes gettimeofday]) AC_CACHE_CHECK([for mach clock_get_time() with wall clock type], erl_cv_mach_clock_get_time_wall, @@ -875,10 +931,12 @@ AC_DEFUN(ERL_WALL_CLOCK, erl_cv_mach_clock_get_time_wall=no) ]) + erl_wall_clock_low_resolution=no erl_wall_clock_id= - case $erl_cv_clock_gettime_wall-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in + case $erl_cv_clock_gettime_wall_$1-$erl_cv_mach_clock_get_time_wall-$ac_cv_func_gettimeofday-$host_os in *-*-*-win32) erl_wall_clock_func=WindowsAPI + erl_wall_clock_low_resolution=yes ;; no-yes-*-*) erl_wall_clock_func=mach_clock_get_time @@ -886,7 +944,13 @@ AC_DEFUN(ERL_WALL_CLOCK, ;; CLOCK_*-*-*-*) erl_wall_clock_func=clock_gettime - erl_wall_clock_id=$erl_cv_clock_gettime_wall + erl_wall_clock_id=$erl_cv_clock_gettime_wall_$1 + for low_res_id in $low_resolution_clock_gettime_wall; do + if test $erl_wall_clock_id = $low_res_id; then + erl_wall_clock_low_resolution=yes + break + fi + done ;; no-no-yes-*) erl_wall_clock_func=gettimeofday @@ -1401,7 +1465,7 @@ AC_ARG_WITH(with_sparc_memory_order, LM_CHECK_THR_LIB ERL_INTERNAL_LIBS -ERL_MONOTONIC_CLOCK +ERL_MONOTONIC_CLOCK(high_resolution) case $erl_monotonic_clock_func in clock_gettime) @@ -2128,20 +2192,89 @@ dnl ---------------------------------------------------------------------- dnl dnl ERL_TIME_CORRECTION dnl -dnl In the presence of a high resolution realtime timer Erlang can adapt -dnl its view of time relative to this timer. On solaris such a timer is -dnl available with the syscall gethrtime(). On other OS's a fallback -dnl solution using times() is implemented. (However on e.g. FreeBSD times() -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 Check for primitives that can be used for implementing +dnl erts_os_monotonic_time() and erts_os_system_time() dnl AC_DEFUN(ERL_TIME_CORRECTION, [ -ERL_WALL_CLOCK +AC_ARG_WITH(clock-resolution, +AS_HELP_STRING([--with-clock-resolution=high|low|default], + [specify wanted clock resolution)])) + +AC_ARG_WITH(clock-gettime-realtime-id, +AS_HELP_STRING([--with-clock-gettime-realtime-id=CLOCKID], + [specify clock id to use with clock_gettime() for realtime time)])) + +AC_ARG_WITH(clock-gettime-monotonic-id, +AS_HELP_STRING([--with-clock-gettime-monotonic-id=CLOCKID], + [specify clock id to use with clock_gettime() for monotonic time)])) + +case "$with_clock_resolution" in + ""|no|yes) + with_clock_resolution=default;; + high|low|default) + ;; + *) + AC_MSG_ERROR([Invalid wanted clock resolution: $with_clock_resolution]) + ;; +esac + +case "$with_clock_gettime_realtime_id" in + ""|no) + with_clock_gettime_realtime_id=no + ;; + CLOCK_*CPUTIME*) + AC_MSG_ERROR([Invalid clock_gettime() realtime clock id: Refusing to use the cputime clock id $with_clock_gettime_realtime_id as realtime clock id]) + ;; + CLOCK_MONOTONIC*|CLOCK_BOOTTIME*|CLOCK_UPTIME*|CLOCK_HIGHRES*) + AC_MSG_ERROR([Invalid clock_gettime() realtime clock id: Refusing to use the monotonic clock id $with_clock_gettime_realtime_id as realtime clock id]) + ;; + CLOCK_*) + ;; + *) + AC_MSG_ERROR([Invalid clock_gettime() clock id: $with_clock_gettime_realtime_id]) + ;; +esac + +case "$with_clock_gettime_monotonic_id" in + ""|no) + with_clock_gettime_monotonic_id=no + ;; + CLOCK_*CPUTIME*) + AC_MSG_ERROR([Invalid clock_gettime() monotonic clock id: Refusing to use the cputime clock id $with_clock_gettime_monotonic_id as monotonic clock id]) + ;; + CLOCK_REALTIME*|CLOCK_TAI*) + AC_MSG_ERROR([Invalid clock_gettime() monotonic clock id: Refusing to use the realtime clock id $with_clock_gettime_monotonic_id as monotonic clock id]) + ;; + CLOCK_*) + ;; + *) + AC_MSG_ERROR([Invalid clock_gettime() clock id: $with_clock_gettime_monotonic_id]) + ;; +esac + +case "$with_clock_resolution-$with_clock_gettime_realtime_id" in + high-no) + ERL_WALL_CLOCK(high_resolution);; + low-no) + ERL_WALL_CLOCK(low_resolution);; + default-no) + ERL_WALL_CLOCK(default_resolution);; + *) + ERL_WALL_CLOCK(custom_resolution, $with_clock_gettime_realtime_id);; +esac + +case "$erl_wall_clock_func-$erl_wall_clock_id-$with_clock_gettime_realtime_id" in + *-*-no) + ;; + clock_gettime-$with_clock_gettime_realtime_id-$with_clock_gettime_realtime_id) + ;; + *) + AC_MSG_ERROR([$with_clock_gettime_realtime_id as clock id to clock_gettime() doesn't compile]) + ;; +esac case $erl_wall_clock_func in mach_clock_get_time) @@ -2162,7 +2295,26 @@ if test "x$erl_wall_clock_id" != "x"; then AC_DEFINE_UNQUOTED(WALL_CLOCK_ID, [$erl_wall_clock_id], [Define to wall clock id to use]) fi -ERL_MONOTONIC_CLOCK +case "$with_clock_resolution-$with_clock_gettime_monotonic_id" in + high-no) + ERL_MONOTONIC_CLOCK(high_resolution);; + low-no) + ERL_MONOTONIC_CLOCK(low_resolution);; + default-no) + ERL_MONOTONIC_CLOCK(default_resolution);; + *) + ERL_MONOTONIC_CLOCK(custom_resolution, $with_clock_gettime_monotonic_id);; +esac + +case "$erl_monotonic_clock_func-$erl_monotonic_clock_id-$with_clock_gettime_monotonic_id" in + *-*-no) + ;; + clock_gettime-$with_clock_gettime_monotonic_id-$with_clock_gettime_monotonic_id) + ;; + *) + AC_MSG_ERROR([$with_clock_gettime_monotonic_id as clock id to clock_gettime() doesn't compile]) + ;; +esac case $erl_monotonic_clock_func in times) @@ -2181,12 +2333,54 @@ case $erl_monotonic_clock_func in ;; esac +if test $erl_corrected_monotonic_clock = yes; then + AC_DEFINE(ERTS_HAVE_CORRECTED_OS_MONOTONIC_TIME, [1], [Define if OS monotonic clock is corrected]) +fi + +if test $erl_monotonic_clock_low_resolution = yes; then + AC_DEFINE(ERTS_HAVE_LOW_RESOLUTION_OS_MONOTONIC_LOW, [1], [Define if you have a low resolution OS monotonic clock]) +fi + xrtlib="$erl_monotonic_clock_lib" if test "x$erl_monotonic_clock_id" != "x"; then AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID_STR, ["$erl_monotonic_clock_id"], [Define as a string of monotonic clock id to use]) AC_DEFINE_UNQUOTED(MONOTONIC_CLOCK_ID, [$erl_monotonic_clock_id], [Define to monotonic clock id to use]) fi +if test $erl_cv_clock_gettime_monotonic_raw = yes; then + AC_DEFINE(HAVE_CLOCK_GETTIME_MONOTONIC_RAW, [1], [Define if you have clock_gettime(CLOCK_MONOTONIC_RAW, _)]) +fi + +ERL_MONOTONIC_CLOCK(high_resolution) + +case $$erl_monotonic_clock_low_resolution-$erl_monotonic_clock_func in + no-mach_clock_get_time) + monotonic_hrtime=yes + AC_DEFINE(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME, [1], [Define if you want to implement erts_os_hrtime() using mach clock_get_time()]) + ;; + no-clock_gettime) + monotonic_hrtime=yes + AC_DEFINE(SYS_HRTIME_USING_CLOCK_GETTIME, [1], [Define if you want to implement erts_os_hrtime() using clock_gettime()]) + ;; + no-gethrtime) + monotonic_hrtime=yes + AC_DEFINE(SYS_HRTIME_USING_GETHRTIME, [1], [Define if you want to implement erts_os_hrtime() using gethrtime()]) + ;; + *) + monotonic_hrtime=no + ;; +esac + +if test $monotonic_hrtime = yes; then + AC_DEFINE(HAVE_MONOTONIC_ERTS_SYS_HRTIME, [1], [Define if you have a monotonic erts_os_hrtime() implementation]) +fi + +if test "x$erl_monotonic_clock_id" != "x"; then + AC_DEFINE_UNQUOTED(HRTIME_CLOCK_ID_STR, ["$erl_monotonic_clock_id"], [Define as a string of monotonic clock id to use]) + AC_DEFINE_UNQUOTED(HRTIME_CLOCK_ID, [$erl_monotonic_clock_id], [Define to monotonic clock id to use]) +fi + + dnl dnl Check if gethrvtime is working, and if to use procfs ioctl dnl or (yet to be written) write to the procfs ctl file. diff --git a/erts/configure.in b/erts/configure.in index 481dfe405e..67d4b713cd 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -317,15 +317,6 @@ if test X"$use_vm_probes" = X"yes"; then [Define to enable VM dynamic trace probes]) fi - -AC_ARG_ENABLE(clock-gettime, -AS_HELP_STRING([--enable-clock-gettime], - [use clock-gettime for time correction]), -[ case "$enableval" in - no) clock_gettime_correction=no ;; - *) clock_gettime_correction=yes ;; - esac ], clock_gettime_correction=unknown) - AC_ARG_WITH(assumed-cache-line-size, AS_HELP_STRING([--with-assumed-cache-line-size=SIZE], [specify assumed cache line size in bytes (valid values are powers of two between and including 16 and 8192; default is 64)])) diff --git a/erts/doc/src/time_correction.xml b/erts/doc/src/time_correction.xml index 979a37d7ff..8af98acc19 100644 --- a/erts/doc/src/time_correction.xml +++ b/erts/doc/src/time_correction.xml @@ -137,6 +137,14 @@ <p>The correctness of time values.</p> </section> + <marker id="Time_Warp"/> + <section> + <title>Time Warp</title> + <p>A time warp is a leap forwards or backwards in time. That + is, the difference of time values taken before and after the + time warp will not correspond to the actual elapsed time.</p> + </section> + <marker id="OS_System_Time"/> <section> <title>OS System Time</title> @@ -146,7 +154,7 @@ <seealso marker="kernel:os#system_time/0"><c>os:system_time()</c></seealso>. This may or may not be an accurate view of POSIX time. This time may typically be adjusted both backwards and forwards without - limitation. That is, huge leaps both backwards and forwards in time + limitation. That is, <seealso marker="#Time_Warp">time warps</seealso> may be observed. You can get information about the Erlang runtime system's source of OS system time by calling <seealso marker="erlang#system_info_os_system_time_source"><c>erlang:system_info(os_system_time_source)</c></seealso>.</p> @@ -159,12 +167,12 @@ system. This time does not leap and have a relatively steady frequency although not completely correct. However, it is not uncommon that the OS monotonic time stops if the system is - suspended. This time typically increase since some unspecified - point in time that is not connected to - <seealso marker="#OS_System_Time">OS system time</seealso>. Note that - this type of time is not necessarily provided by all operating - systems. You can get information about the Erlang runtime - system's source of OS monotonic time by calling + suspended. This time typically increase since some + unspecified point in time that is not connected to + <seealso marker="#OS_System_Time">OS system time</seealso>. Note + that this type of time is not necessarily provided by all + operating systems. You can get information about the Erlang + runtime system's source of OS monotonic time by calling <seealso marker="erlang#system_info_os_monotonic_time_source"><c>erlang:system_info(os_monotonic_time_source)</c></seealso>.</p> </section> @@ -177,9 +185,11 @@ <seealso marker="erlang#system_time/0"><c>erlang:system_time()</c></seealso>. This time may or may not be an accurate view of POSIX time, and may or may not align with <seealso marker="#OS_System_Time">OS system - time</seealso>. The <seealso marker="#Time_Warp_Modes">time - warp mode</seealso> determines how it behaves when OS system - time suddenly change.</p> + time</seealso>. The runtime system works towards aligning the two + system times. Depending on <seealso marker="#Time_Warp_Modes">time + warp mode</seealso> used, this may be achieved by letting the Erlang + system time perform a <seealso marker="#Time_Warp">time + warp</seealso>.</p> </section> <marker id="Erlang_Monotonic_Time"/> @@ -219,12 +229,6 @@ </p> </section> - <marker id="Time_Warp"/> - <section> - <title>Time Warp</title> - <p>A time warp is a leap forwards or backwards in time.</p> - </section> - </section> <section> @@ -332,7 +336,7 @@ <section> <title>Time Warp Safe Code</title> <p>Time warp safe code is code that is able to handle - a time warp of + a <seealso marker="#Time_Warp">time warp</seealso> of <seealso marker="#Erlang_System_Time">Erlang system time</seealso>. </p> @@ -378,11 +382,11 @@ <p>The time offset is determined at runtime system start and will after this not change. This is the default behavior. Not because it is the best mode (which it isn't). It is - default only because this is how the runtime system always - has behaved until ERTS version 7.0, and you have to ensure - that your Erlang code that may execute during a time warp is - <seealso marker="#Time_Warp_Safe_Code">time warp safe</seealso> - before you can enable other modes.</p> + default <em>only</em> because this is how the runtime system + always has behaved up until ERTS version 7.0, and you have to + ensure that your Erlang code that may execute during a time + warp is <seealso marker="#Time_Warp_Safe_Code">time warp + safe</seealso> before you can enable other modes.</p> <p>Since the time offset is not allowed to change, time correction needs to adjust the frequency of the Erlang @@ -422,9 +426,9 @@ system time has been corrected, you may want to use the single time warp mode. Note that there are limitations to when you can execute time warp unsafe code using this mode. If it is possible - to only utilize time warp safe code, it is much better to use - the <seealso marker="#Multi_Time_Warp_Mode">multi time warp - mode</seealso> instead. + to only utilize time warp safe code, it is <em>much</em> better + to use the <seealso marker="#Multi_Time_Warp_Mode">multi time + warp mode</seealso> instead. </p> <p>Using the single time warp mode, the time offset is @@ -438,12 +442,14 @@ current OS system time is determined. This offset will from now on be fixed during the whole preliminary phase.</p> - <p>If time correction is enabled, the Erlang - monotonic clock will only use the OS monotonic time as - time source during this phase. That is, during the - preliminary phase changes in OS system time will have - no effect on Erlang system time and/or Erlang - monotonic time what so ever.</p> + <p>If time correction is enabled, adjustments to the + Erlang monotonic clock will be made to keep its + frequency as correct as possible, but <em>no</em> + adjustments will be made trying to align Erlang system + time and OS system time. That is, during the preliminary + Erlang system time and OS system time might diverge + from each other, and no attempt to prevent this will + be made.</p> <p>If time correction is disabled, changes in OS system time will effect the monotonic clock the same way as @@ -462,15 +468,16 @@ <p>During finalization, the time offset is adjusted and fixated so that current Erlang system time align with - current OS system time. Since the time offset - may be changed, the Erlang system time may do - a time warp at this point. The time offset will from - now on be fixed until the runtime system terminates. - If time correction has been enabled, the time correction - also begins when this phase begins. When the system is - in the final phase it behaves exactly as in the - <seealso marker="#No_Time_Warp_Mode">no time warp - mode</seealso>.</p> + current OS system time. Since the time offset may + change during the finalization, the Erlang system time + may do a time warp at this point. The time offset will + from now on be fixed until the runtime system terminates. + If time correction has been enabled, the time + correction will from now on also make adjustments + in order to align Erlang system time with OS system + time. When the system is in the final phase it behaves + exactly as in the <seealso marker="#No_Time_Warp_Mode">no + time warp mode</seealso>.</p> </item> </taglist> diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 4a116c0740..307f9c93e0 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -404,7 +404,7 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) Uint reclaimed_now = 0; int done = 0; int off_heap_msgs; - Uint ms1, s1, us1; + ErtsMonotonicTime start_time = 0; /* Shut up faulty warning... */ erts_aint32_t state; ErtsSchedulerData *esdp; #ifdef USE_VM_PROBES @@ -424,9 +424,8 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) state = erts_smp_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC); off_heap_msgs = state & ERTS_PSFLG_OFF_HEAP_MSGS; - if (erts_system_monitor_long_gc != 0) { - get_now(&ms1, &s1, &us1); - } + if (erts_system_monitor_long_gc != 0) + start_time = erts_get_monotonic_time(); ERTS_CHK_OFFHEAP(p); @@ -474,16 +473,14 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj) } if (erts_system_monitor_long_gc != 0) { - Uint ms2, s2, us2; - Sint t; + ErtsMonotonicTime end_time; + Uint gc_time; if (erts_test_long_gc_sleep) while (0 != erts_milli_sleep(erts_test_long_gc_sleep)); - get_now(&ms2, &s2, &us2); - t = ms2 - ms1; - t = t*1000000 + s2 - s1; - t = t*1000 + ((Sint) (us2 - us1))/1000; - if (t > 0 && (Uint)t > erts_system_monitor_long_gc) { - monitor_long_gc(p, t); + end_time = erts_get_monotonic_time(); + gc_time = (Uint) ERTS_MONOTONIC_TO_MSEC(end_time - start_time); + if (gc_time && gc_time > erts_system_monitor_long_gc) { + monitor_long_gc(p, gc_time); } } if (erts_system_monitor_large_heap != 0) { diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index cb7764addc..5af3c21d40 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -26,6 +26,12 @@ #define ERTS_TIME_ASSERT(B) ((void) 1) #endif +typedef enum { + ERTS_NO_TIME_WARP_MODE, + ERTS_SINGLE_TIME_WARP_MODE, + ERTS_MULTI_TIME_WARP_MODE +} ErtsTimeWarpMode; + typedef struct ErtsTimerWheel_ ErtsTimerWheel; typedef erts_atomic64_t * ErtsNextTimeoutRef; extern ErtsTimerWheel *erts_default_timer_wheel; @@ -38,7 +44,6 @@ extern SysTimeval erts_first_emu_time; typedef struct erl_timer { struct erl_timer* next; /* next entry tiw slot or chain */ struct erl_timer* prev; /* prev entry tiw slot or chain */ - Uint slot; /* slot in timer wheel */ erts_smp_atomic_t wheel; ErtsMonotonicTime timeout_pos; /* Timeout in absolute clock ticks */ /* called when timeout */ @@ -46,6 +51,7 @@ typedef struct erl_timer { /* called when cancel (may be NULL) */ void (*cancel)(void*); void* arg; /* argument to timeout/cancel procs */ + int slot; /* slot in timer wheel */ } ErlTimer; typedef void (*ErlTimeoutProc)(void*); @@ -77,6 +83,7 @@ void erts_cancel_smp_ptimer(ErtsSmpPTimer *ptimer); void erts_monitor_time_offset(Eterm id, Eterm ref); int erts_demonitor_time_offset(Eterm ref); +int erts_init_time_sup(int, ErtsTimeWarpMode); void erts_late_init_time_sup(void); /* timer-wheel api */ diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index bbdedcc128..9d572c0b0a 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -133,6 +133,7 @@ struct time_sup_read_only__ { ErtsTimeWarpMode warp_mode; #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT ErtsMonotonicTime moffset; + int os_corrected_monotonic_time; int os_monotonic_time_disable; char *os_monotonic_time_func; char *os_monotonic_time_clock_id; @@ -159,12 +160,16 @@ struct time_sup_read_only__ { ErtsMonotonicTime large_diff; ErtsMonotonicTime small_diff; } adj; + struct { + ErtsMonotonicTime error; + ErtsMonotonicTime resolution; + int intervals; + int use_avg; + } drift_adj; }; typedef struct { -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC ErtsMonotonicTime drift; /* Correction for os monotonic drift */ -#endif ErtsMonotonicTime error; /* Correction for error between system times */ } ErtsMonotonicCorrection; @@ -174,7 +179,7 @@ typedef struct { ErtsMonotonicCorrection correction; } ErtsMonotonicCorrectionInstance; -#define ERTS_DRIFT_INTERVALS 5 +#define ERTS_MAX_DRIFT_INTERVALS 50 typedef struct { struct { struct { @@ -185,7 +190,7 @@ typedef struct { ErtsMonotonicTime sys; ErtsMonotonicTime mon; } time; - } intervals[ERTS_DRIFT_INTERVALS]; + } intervals[ERTS_MAX_DRIFT_INTERVALS]; struct { ErtsMonotonicTime sys; ErtsMonotonicTime mon; @@ -197,9 +202,7 @@ typedef struct { typedef struct { ErtsMonotonicCorrectionInstance prev; ErtsMonotonicCorrectionInstance curr; -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC ErtsMonotonicDriftData drift; -#endif ErtsMonotonicTime last_check; int short_check_interval; } ErtsMonotonicCorrectionData; @@ -213,10 +216,11 @@ struct time_sup_infrequently_changed__ { } parmon; ErtsMonotonicTime minit; #endif - int finalized_offset; ErtsSystemTime sinit; ErtsMonotonicTime not_corrected_moffset; - erts_atomic64_t offset; + erts_smp_atomic64_t offset; + ErtsMonotonicTime shadow_offset; + erts_smp_atomic32_t preliminary_offset; }; struct time_sup_frequently_changed__ { @@ -254,19 +258,19 @@ erts_get_approx_time(void) static ERTS_INLINE void init_time_offset(ErtsMonotonicTime offset) { - erts_atomic64_init_nob(&time_sup.inf.c.offset, (erts_aint64_t) offset); + erts_smp_atomic64_init_nob(&time_sup.inf.c.offset, (erts_aint64_t) offset); } static ERTS_INLINE void set_time_offset(ErtsMonotonicTime offset) { - erts_atomic64_set_relb(&time_sup.inf.c.offset, (erts_aint64_t) offset); + erts_smp_atomic64_set_relb(&time_sup.inf.c.offset, (erts_aint64_t) offset); } static ERTS_INLINE ErtsMonotonicTime get_time_offset(void) { - return (ErtsMonotonicTime) erts_atomic64_read_acqb(&time_sup.inf.c.offset); + return (ErtsMonotonicTime) erts_smp_atomic64_read_acqb(&time_sup.inf.c.offset); } @@ -290,16 +294,38 @@ get_time_offset(void) #define ERTS_TIME_DRIFT_MAX_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(50) #define ERTS_TIME_DRIFT_MIN_ADJ_DIFF ERTS_USEC_TO_MONOTONIC(5) +/* + * Maximum drift of the OS monotonic clock expected. + * + * We use 1 milli second per second. If the monotonic + * clock drifts more than this we will fail to adjust for + * drift, and error correction will kick in instead. + * If it is larger than this, one could argue that the + * primitive is to poor to be used... + */ +#define ERTS_MAX_MONOTONIC_DRIFT ERTS_MSEC_TO_MONOTONIC(1) + +/* + * We assume that precision is 32 times worse than the + * resolution. This is a wild guess, but there are no + * practical way to determine actual precision. + */ +#define ERTS_ASSUMED_PRECISION_DROP 32 + +#define ERTS_MIN_MONOTONIC_DRIFT_MEASUREMENT \ + (ERTS_SHORT_TIME_CORRECTION_CHECK - 2*ERTS_MAX_MONOTONIC_DRIFT) + + static ERTS_INLINE ErtsMonotonicTime calc_corrected_erl_mtime(ErtsMonotonicTime os_mtime, ErtsMonotonicCorrectionInstance *cip, - ErtsMonotonicTime *os_mdiff_p) + ErtsMonotonicTime *os_mdiff_p, + int os_drift_corrected) { ErtsMonotonicTime erl_mtime, diff = os_mtime - cip->os_mtime; ERTS_TIME_ASSERT(diff >= 0); -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC - diff += (cip->correction.drift*diff)/ERTS_MONOTONIC_TIME_UNIT; -#endif + if (!os_drift_corrected) + diff += (cip->correction.drift*diff)/ERTS_MONOTONIC_TIME_UNIT; erl_mtime = cip->erl_mtime; erl_mtime += diff; erl_mtime += cip->correction.error*(diff/ERTS_TCORR_ERR_UNIT); @@ -308,7 +334,8 @@ calc_corrected_erl_mtime(ErtsMonotonicTime os_mtime, return erl_mtime; } -static ErtsMonotonicTime get_corrected_time(void) +static ERTS_INLINE ErtsMonotonicTime +read_corrected_time(int os_drift_corrected) { ErtsMonotonicTime os_mtime; ErtsMonotonicCorrectionData cdata; @@ -331,7 +358,18 @@ static ErtsMonotonicTime get_corrected_time(void) cip = &cdata.prev; } - return calc_corrected_erl_mtime(os_mtime, cip, NULL); + return calc_corrected_erl_mtime(os_mtime, cip, NULL, + os_drift_corrected); +} + +static ErtsMonotonicTime get_os_drift_corrected_time(void) +{ + return read_corrected_time(!0); +} + +static ErtsMonotonicTime get_corrected_time(void) +{ + return read_corrected_time(0); } #ifdef ERTS_TIME_CORRECTION_PRINT @@ -352,66 +390,42 @@ print_correction(int change, usec_sdiff = ERTS_MONOTONIC_TO_USEC(sdiff); if (!change) - fprintf(stderr, - "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppb] : " - "tmo = %lld msec\r\n", - (long long) usec_sdiff, - (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, - (long long) tmo); + erts_fprintf(stderr, + "sdiff = %b64d usec : [ec=%b64d ppm, dc=%b64d ppb] : " + "tmo = %bpu msec\r\n", + usec_sdiff, + (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, + (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + tmo); else - fprintf(stderr, - "sdiff = %lld usec : [ec=%lld ppm, dc=%lld ppb] " - "-> [ec=%lld ppm, dc=%lld ppb] : tmo = %lld msec\r\n", - (long long) usec_sdiff, - (long long) (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, - (long long) (1000000*new_ecorr) / ERTS_TCORR_ERR_UNIT, - (long long) (1000000000*new_dcorr) / ERTS_MONOTONIC_TIME_UNIT, - (long long) tmo); - + erts_fprintf(stderr, + "sdiff = %b64d usec : [ec=%b64d ppm, dc=%b64d ppb] " + "-> [ec=%b64d ppm, dc=%b64d ppb] : tmo = %bpu msec\r\n", + usec_sdiff, + (1000000*old_ecorr) / ERTS_TCORR_ERR_UNIT, + (1000000000*old_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + (1000000*new_ecorr) / ERTS_TCORR_ERR_UNIT, + (1000000000*new_dcorr) / ERTS_MONOTONIC_TIME_UNIT, + tmo); } #endif static void -check_time_correction(void *unused) +check_time_correction(void *idap) { -#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 + UWord init_drift_adj = (UWord) idap; ErtsMonotonicCorrectionData cdata; ErtsMonotonicCorrection new_correction; ErtsMonotonicCorrectionInstance *cip; ErtsMonotonicTime mdiff, sdiff, os_mtime, erl_mtime, os_stime, erl_stime, time_offset; Uint timeout; - int set_new_correction, begin_short_intervals = 0; + int os_drift_corrected = time_sup.r.o.os_corrected_monotonic_time; + int set_new_correction = 0, begin_short_intervals = 0; erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); - ASSERT(time_sup.inf.c.finalized_offset); - erts_os_times(&os_mtime, &os_stime); cdata = time_sup.inf.c.parmon.cdata; @@ -423,14 +437,23 @@ check_time_correction(void *unused) "OS monotonic time stepped backwards\n"); cip = &cdata.curr; - erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, &mdiff); + erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, &mdiff, + os_drift_corrected); time_offset = get_time_offset(); erl_stime = erl_mtime + time_offset; sdiff = erl_stime - os_stime; + if (time_sup.inf.c.shadow_offset) { + ERTS_TIME_ASSERT(time_sup.r.o.warp_mode == ERTS_SINGLE_TIME_WARP_MODE); + if (erts_smp_atomic32_read_nob(&time_sup.inf.c.preliminary_offset)) + sdiff += time_sup.inf.c.shadow_offset; + else + time_sup.inf.c.shadow_offset = 0; + } + new_correction = cip->correction; - + if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE && (sdiff < -2*time_sup.r.o.adj.small_diff || 2*time_sup.r.o.adj.small_diff < sdiff)) { @@ -440,9 +463,24 @@ check_time_correction(void *unused) set_time_offset(time_offset); schedule_send_time_offset_changed_notifications(time_offset); begin_short_intervals = 1; - if (cdata.curr.correction.error == 0) - set_new_correction = 0; - else { + if (cdata.curr.correction.error != 0) { + set_new_correction = 1; + new_correction.error = 0; + } + } + else if ((time_sup.r.o.warp_mode == ERTS_SINGLE_TIME_WARP_MODE + && erts_smp_atomic32_read_nob(&time_sup.inf.c.preliminary_offset)) + && (sdiff < -2*time_sup.r.o.adj.small_diff + || 2*time_sup.r.o.adj.small_diff < sdiff)) { + /* + * System time diff exeeded limits; change shadow offset + * and let OS system time leap away from Erlang system + * time. + */ + time_sup.inf.c.shadow_offset -= sdiff; + sdiff = 0; + begin_short_intervals = 1; + if (cdata.curr.correction.error != 0) { set_new_correction = 1; new_correction.error = 0; } @@ -462,16 +500,11 @@ check_time_correction(void *unused) else new_correction.error = -ERTS_TCORR_ERR_SMALL_ADJ; } - else { - set_new_correction = 0; - } } else if (cdata.curr.correction.error > 0) { if (sdiff < 0) { - if (cdata.curr.correction.error == ERTS_TCORR_ERR_LARGE_ADJ - || -time_sup.r.o.adj.large_diff <= sdiff) - set_new_correction = 0; - else { + if (cdata.curr.correction.error != ERTS_TCORR_ERR_LARGE_ADJ + && sdiff < -time_sup.r.o.adj.large_diff) { new_correction.error = ERTS_TCORR_ERR_LARGE_ADJ; set_new_correction = 1; } @@ -490,14 +523,11 @@ 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 <= time_sup.r.o.adj.large_diff) - set_new_correction = 0; - else { + if (cdata.curr.correction.error != -ERTS_TCORR_ERR_LARGE_ADJ + && time_sup.r.o.adj.large_diff < sdiff) { new_correction.error = -ERTS_TCORR_ERR_LARGE_ADJ; set_new_correction = 1; } - set_new_correction = 0; } else if (sdiff < -time_sup.r.o.adj.small_diff) { set_new_correction = 1; @@ -512,8 +542,7 @@ check_time_correction(void *unused) } } -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC - { + if (!os_drift_corrected) { ErtsMonotonicDriftData *ddp = &time_sup.inf.c.parmon.cdata.drift; int ix = ddp->ix; ErtsMonotonicTime mtime_diff, old_os_mtime; @@ -521,25 +550,26 @@ check_time_correction(void *unused) old_os_mtime = ddp->intervals[ix].time.mon; mtime_diff = os_mtime - old_os_mtime; - if (mtime_diff >= ERTS_SEC_TO_MONOTONIC(10)) { + if ((mtime_diff >= ERTS_MIN_MONOTONIC_DRIFT_MEASUREMENT) + | init_drift_adj) { ErtsMonotonicTime drift_adj, drift_adj_diff, old_os_stime, - stime_diff, mtime_acc, stime_acc, avg_drift_adj; + smtime_diff, stime_diff, mtime_acc, stime_acc, + avg_drift_adj, max_drift; old_os_stime = ddp->intervals[ix].time.sys; mtime_acc = ddp->acc.mon; stime_acc = ddp->acc.sys; - avg_drift_adj = (((stime_acc - mtime_acc)*ERTS_MONOTONIC_TIME_UNIT) + 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); - + smtime_diff = stime_diff - mtime_diff; ix++; - if (ix >= ERTS_DRIFT_INTERVALS) + if (ix >= time_sup.r.o.drift_adj.intervals) ix = 0; mtime_acc -= ddp->intervals[ix].diff.mon; mtime_acc += mtime_diff; @@ -555,24 +585,50 @@ check_time_correction(void *unused) ddp->acc.mon = mtime_acc; ddp->acc.sys = stime_acc; - drift_adj_diff = avg_drift_adj - drift_adj; - if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF - || ERTS_TIME_DRIFT_MAX_ADJ_DIFF < drift_adj_diff) { - ddp->dirty_counter = ERTS_DRIFT_INTERVALS; + max_drift = ERTS_MAX_MONOTONIC_DRIFT; + max_drift *= ERTS_MONOTONIC_TO_SEC(mtime_diff); + + if (smtime_diff > time_sup.r.o.drift_adj.error + max_drift + || smtime_diff < -1*time_sup.r.o.drift_adj.error - max_drift) { + dirty_intervals: + /* + * We had a leap in system time. Mark array as + * dirty to ensure that dirty values are rotated + * out before we use it again... + */ + ddp->dirty_counter = time_sup.r.o.drift_adj.intervals; begin_short_intervals = 1; } - else { - if (ddp->dirty_counter <= 0) { - drift_adj = ((stime_acc - mtime_acc) - *ERTS_MONOTONIC_TIME_UNIT) / mtime_acc; + else if (ddp->dirty_counter > 0) { + if (init_drift_adj) { + new_correction.drift = ((smtime_diff + * ERTS_MONOTONIC_TIME_UNIT) + / mtime_diff); + set_new_correction = 1; } - if (ddp->dirty_counter >= 0) { - if (ddp->dirty_counter == 0) { - /* Force set new drift correction... */ - set_new_correction = 1; - } + ddp->dirty_counter--; + } + else { + if (ddp->dirty_counter == 0) { + /* Force set new drift correction... */ + set_new_correction = 1; ddp->dirty_counter--; } + + if (time_sup.r.o.drift_adj.use_avg) + drift_adj = (((stime_acc - mtime_acc) + * ERTS_MONOTONIC_TIME_UNIT) + / mtime_acc); + else + drift_adj = ((smtime_diff + * ERTS_MONOTONIC_TIME_UNIT) + / mtime_diff); + + drift_adj_diff = avg_drift_adj - drift_adj; + if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF + || ERTS_TIME_DRIFT_MAX_ADJ_DIFF < drift_adj_diff) + goto dirty_intervals; + drift_adj_diff = drift_adj - new_correction.drift; if (drift_adj_diff) { if (drift_adj_diff > ERTS_TIME_DRIFT_MAX_ADJ_DIFF) @@ -580,7 +636,6 @@ check_time_correction(void *unused) else if (drift_adj_diff < -ERTS_TIME_DRIFT_MAX_ADJ_DIFF) drift_adj_diff = -ERTS_TIME_DRIFT_MAX_ADJ_DIFF; new_correction.drift += drift_adj_diff; - if (drift_adj_diff < -ERTS_TIME_DRIFT_MIN_ADJ_DIFF || ERTS_TIME_DRIFT_MIN_ADJ_DIFF < drift_adj_diff) { set_new_correction = 1; @@ -589,7 +644,6 @@ check_time_correction(void *unused) } } } -#endif begin_short_intervals |= set_new_correction; @@ -608,25 +662,34 @@ check_time_correction(void *unused) timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK); else { ErtsMonotonicTime ecorr = new_correction.error; - if (sdiff < 0) - sdiff = -1*sdiff; + ErtsMonotonicTime abs_sdiff; + abs_sdiff = (sdiff < 0) ? -1*sdiff : sdiff; if (ecorr < 0) ecorr = -1*ecorr; - if (sdiff > ecorr*(ERTS_LONG_TIME_CORRECTION_CHECK/ERTS_TCORR_ERR_UNIT)) + if (abs_sdiff > ecorr*(ERTS_LONG_TIME_CORRECTION_CHECK/ERTS_TCORR_ERR_UNIT)) timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK); else { - timeout = ERTS_MONOTONIC_TO_MSEC((ERTS_TCORR_ERR_UNIT*sdiff)/ecorr); + timeout = ERTS_MONOTONIC_TO_MSEC((ERTS_TCORR_ERR_UNIT*abs_sdiff)/ecorr); if (timeout < 10) timeout = 10; } } if (timeout > ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK) - && time_sup.inf.c.parmon.cdata.short_check_interval) { + && (time_sup.inf.c.parmon.cdata.short_check_interval + || time_sup.inf.c.parmon.cdata.drift.dirty_counter >= 0)) { timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK); } - ERTS_PRINT_CORRECTION; +#ifdef ERTS_TIME_CORRECTION_PRINT + print_correction(set_new_correction, + sdiff, + cip->correction.error, + cip->correction.drift, + new_correction.error, + new_correction.drift, + timeout); +#endif if (set_new_correction) { erts_smp_rwmtx_rwlock(&time_sup.inf.c.parmon.rwmtx); @@ -638,16 +701,17 @@ check_time_correction(void *unused) /* * Current correction instance begin when - * OS monotonic time has increased one unit. + * OS monotonic time has increased two units. */ - os_mtime++; + os_mtime += 2; /* * Erlang monotonic time corresponding to * next OS monotonic time using previous * correction. */ - erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, NULL); + erl_mtime = calc_corrected_erl_mtime(os_mtime, cip, NULL, + os_drift_corrected); /* * Save new current correction instance. @@ -664,18 +728,60 @@ check_time_correction(void *unused) NULL, NULL, timeout); +} -#undef ERTS_PRINT_CORRECTION +static ErtsMonotonicTime get_os_corrected_time(void) +{ + ASSERT(time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE); + return erts_os_monotonic_time() + time_sup.r.o.moffset; } -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC +static void +check_time_offset(void *unused) +{ + ErtsMonotonicTime sdiff, os_mtime, erl_mtime, os_stime, + erl_stime, time_offset; + + ASSERT(time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE); + + erts_os_times(&os_mtime, &os_stime); + + erl_mtime = os_mtime + time_sup.r.o.moffset; + time_offset = get_time_offset(); + erl_stime = erl_mtime + time_offset; + + sdiff = erl_stime - os_stime; + + if ((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... */ +#ifdef ERTS_TIME_CORRECTION_PRINT + erts_fprintf(stderr, "sdiff = %b64d nsec -> 0 nsec\n", + ERTS_MONOTONIC_TO_NSEC(sdiff)); +#endif + time_offset -= sdiff; + sdiff = 0; + set_time_offset(time_offset); + schedule_send_time_offset_changed_notifications(time_offset); + } +#ifdef ERTS_TIME_CORRECTION_PRINT + else erts_fprintf(stderr, "sdiff = %b64d nsec\n", + ERTS_MONOTONIC_TO_NSEC(sdiff)); +#endif + + erts_set_timer(&time_sup.inf.c.parmon.timer, + check_time_offset, + NULL, + NULL, + ERTS_MONOTONIC_TO_MSEC(ERTS_LONG_TIME_CORRECTION_CHECK)); +} static void -init_check_time_correction(void *unused) +init_check_time_correction(void *quick_init_drift) { ErtsMonotonicDriftData *ddp; ErtsMonotonicTime old_mtime, old_stime, mtime, stime, mtime_diff, - stime_diff; + stime_diff, smtime_diff, max_drift; int ix; ddp = &time_sup.inf.c.parmon.cdata.drift; @@ -687,7 +793,13 @@ init_check_time_correction(void *unused) mtime_diff = mtime - old_mtime; stime_diff = stime - old_stime; - if (100*stime_diff < 80*mtime_diff || 120*mtime_diff < 100*stime_diff ) { + smtime_diff = stime_diff - mtime_diff; + + max_drift = ERTS_MAX_MONOTONIC_DRIFT; + max_drift *= ERTS_MONOTONIC_TO_SEC(mtime_diff); + + if (smtime_diff > time_sup.r.o.drift_adj.error + max_drift + || smtime_diff < -1*time_sup.r.o.drift_adj.error - max_drift) { /* Had a system time leap... pretend no drift... */ stime_diff = mtime_diff; } @@ -697,29 +809,28 @@ init_check_time_correction(void *unused) * a drift adjustment, and repeat this interval * in all slots... */ - for (ix = 0; ix < ERTS_DRIFT_INTERVALS; ix++) { + for (ix = 0; ix < time_sup.r.o.drift_adj.intervals; ix++) { ddp->intervals[ix].diff.mon = mtime_diff; ddp->intervals[ix].diff.sys = stime_diff; ddp->intervals[ix].time.mon = old_mtime; ddp->intervals[ix].time.sys = old_stime; } - ddp->acc.sys = stime_diff*ERTS_DRIFT_INTERVALS; - ddp->acc.mon = mtime_diff*ERTS_DRIFT_INTERVALS; + ddp->acc.sys = stime_diff*time_sup.r.o.drift_adj.intervals; + ddp->acc.mon = mtime_diff*time_sup.r.o.drift_adj.intervals; ddp->ix = 0; - ddp->dirty_counter = ERTS_DRIFT_INTERVALS; + ddp->dirty_counter = time_sup.r.o.drift_adj.intervals; - check_time_correction(NULL); + check_time_correction(quick_init_drift); } -#endif - static ErtsMonotonicTime finalize_corrected_time_offset(ErtsSystemTime *stimep) { ErtsMonotonicTime os_mtime; ErtsMonotonicCorrectionData cdata; ErtsMonotonicCorrectionInstance *cip; + int os_drift_corrected = time_sup.r.o.os_corrected_monotonic_time; erts_smp_rwmtx_rlock(&time_sup.inf.c.parmon.rwmtx); @@ -734,25 +845,38 @@ finalize_corrected_time_offset(ErtsSystemTime *stimep) "OS monotonic time stepped backwards\n"); cip = &cdata.curr; - return calc_corrected_erl_mtime(os_mtime, cip, NULL); + return calc_corrected_erl_mtime(os_mtime, cip, NULL, + os_drift_corrected); } static void late_init_time_correction(void) { - if (time_sup.inf.c.finalized_offset) { + Uint timeout; + Uint quick_init_drift_adj; + void (*check_func)(void *); - erts_init_timer(&time_sup.inf.c.parmon.timer); - erts_set_timer(&time_sup.inf.c.parmon.timer, -#ifndef ERTS_HAVE_CORRECTED_OS_MONOTONIC - init_check_time_correction, -#else - check_time_correction, -#endif - NULL, - NULL, - ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK)); - } + quick_init_drift_adj = + (Uint) ERTS_MONOTONIC_TO_USEC(time_sup.r.o.drift_adj.error) == 0; + + if (quick_init_drift_adj) + timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK/10); + else + timeout = ERTS_MONOTONIC_TO_MSEC(ERTS_SHORT_TIME_CORRECTION_CHECK); + + if (!time_sup.r.o.os_corrected_monotonic_time) + check_func = init_check_time_correction; + else if (time_sup.r.o.get_time == get_os_corrected_time) + check_func = check_time_offset; + else + check_func = check_time_correction; + + erts_init_timer(&time_sup.inf.c.parmon.timer); + erts_set_timer(&time_sup.inf.c.parmon.timer, + check_func, + NULL, + (void *) quick_init_drift_adj, + timeout); } #endif /* ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT */ @@ -832,6 +956,8 @@ void erts_init_sys_time_sup(void) #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT time_sup.r.o.os_monotonic_time_disable = !sys_init_time_res.have_os_monotonic_time; + time_sup.r.o.os_corrected_monotonic_time = + sys_init_time_res.have_corrected_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 @@ -856,7 +982,7 @@ void erts_init_sys_time_sup(void) int erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) { - ErtsMonotonicTime resolution; + ErtsMonotonicTime resolution, ilength, intervals, short_isecs; #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT ErtsMonotonicTime abs_native_offset, native_offset; #endif @@ -870,9 +996,10 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) time_sup.r.o.warp_mode = time_warp_mode; if (time_warp_mode == ERTS_SINGLE_TIME_WARP_MODE) - time_sup.inf.c.finalized_offset = 0; + erts_smp_atomic32_init_nob(&time_sup.inf.c.preliminary_offset, 1); else - time_sup.inf.c.finalized_offset = ~0; + erts_smp_atomic32_init_nob(&time_sup.inf.c.preliminary_offset, 0); + time_sup.inf.c.shadow_offset = 0; #if !ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT @@ -882,7 +1009,7 @@ 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; native_offset = time_sup.r.o.start - ERTS_MONOTONIC_TIME_UNIT; - native_offset = native_offset; + abs_native_offset = native_offset; #else /* ARCH_64 */ if (ERTS_MONOTONIC_TIME_UNIT <= 10*1000*1000) { time_sup.r.o.start = 0; @@ -938,17 +1065,66 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) 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; + time_sup.r.o.drift_adj.resolution = resolution; + + if (time_sup.r.o.os_corrected_monotonic_time) { + time_sup.r.o.drift_adj.use_avg = 0; + time_sup.r.o.drift_adj.intervals = 0; + time_sup.r.o.drift_adj.error = 0; + time_sup.inf.c.parmon.cdata.drift.dirty_counter = -1; + } + else { + /* + * Calculate length of the interval in seconds needed + * in order to get an error that is at most 1 micro second. + * If this interval is longer than the short time correction + * check interval we use the average of all values instead + * of the latest value. + */ + short_isecs = ERTS_MONOTONIC_TO_SEC(ERTS_SHORT_TIME_CORRECTION_CHECK); + ilength = ERTS_ASSUMED_PRECISION_DROP * ERTS_MONOTONIC_TIME_UNIT; + ilength /= (resolution * ERTS_USEC_TO_MONOTONIC(1)); + time_sup.r.o.drift_adj.use_avg = ilength > short_isecs; + + if (ilength == 0) + intervals = 5; + else { + intervals = ilength / short_isecs; + if (intervals > ERTS_MAX_DRIFT_INTERVALS) + intervals = ERTS_MAX_DRIFT_INTERVALS; + else if (intervals < 5) + intervals = 5; + } + time_sup.r.o.drift_adj.intervals = (int) intervals; + + /* + * drift_adj.error equals maximum assumed error + * over a short time interval. We use this value also + * when examining a large interval. In this case the + * error will be smaller, but we do not want to + * recalculate this over and over again. + */ + + time_sup.r.o.drift_adj.error = ERTS_MONOTONIC_TIME_UNIT; + time_sup.r.o.drift_adj.error *= ERTS_ASSUMED_PRECISION_DROP; + time_sup.r.o.drift_adj.error /= resolution * short_isecs; + } #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)); + erts_fprintf(stderr, "resolution = %b64d\n", resolution); + erts_fprintf(stderr, "adj large diff = %b64d usec\n", + ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.large_diff)); + erts_fprintf(stderr, "adj small diff = %b64d usec\n", + ERTS_MONOTONIC_TO_USEC(time_sup.r.o.adj.small_diff)); + if (!time_sup.r.o.os_corrected_monotonic_time) { + erts_fprintf(stderr, "drift intervals = %d\n", + time_sup.r.o.drift_adj.intervals); + erts_fprintf(stderr, "drift adj error = %b64d usec\n", + ERTS_MONOTONIC_TO_USEC(time_sup.r.o.drift_adj.error)); + erts_fprintf(stderr, "drift adj max diff = %b64d nsec\n", + ERTS_MONOTONIC_TO_NSEC(ERTS_TIME_DRIFT_MAX_ADJ_DIFF)); + erts_fprintf(stderr, "drift adj min diff = %b64d nsec\n", + ERTS_MONOTONIC_TO_NSEC(ERTS_TIME_DRIFT_MIN_ADJ_DIFF)); + } #endif if (ERTS_MONOTONIC_TIME_UNIT < ERTS_CLKTCK_RESOLUTION) @@ -967,6 +1143,7 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) erts_os_times(&time_sup.inf.c.minit, &time_sup.inf.c.sinit); time_sup.r.o.moffset = -1*time_sup.inf.c.minit; + time_sup.r.o.moffset += ERTS_MONOTONIC_TIME_UNIT; offset = time_sup.inf.c.sinit; offset -= ERTS_MONOTONIC_TIME_UNIT; init_time_offset(offset); @@ -979,11 +1156,9 @@ 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 = 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 = ERTS_MONOTONIC_TIME_UNIT; cdatap->curr.os_mtime = time_sup.inf.c.minit; @@ -991,7 +1166,12 @@ erts_init_time_sup(int time_correction, ErtsTimeWarpMode time_warp_mode) cdatap->short_check_interval = ERTS_INIT_SHORT_INTERVAL_COUNTER; cdatap->prev = cdatap->curr; - time_sup.r.o.get_time = get_corrected_time; + if (!time_sup.r.o.os_corrected_monotonic_time) + time_sup.r.o.get_time = get_corrected_time; + else if (time_sup.r.o.warp_mode == ERTS_MULTI_TIME_WARP_MODE) + time_sup.r.o.get_time = get_os_corrected_time; + else + time_sup.r.o.get_time = get_os_drift_corrected_time; } else #endif @@ -1020,8 +1200,8 @@ void erts_late_init_time_sup(void) { #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT - /* Timer wheel must be initialized */ - if (time_sup.r.o.get_time == get_corrected_time) + /* Timer wheel must have beeen initialized */ + if (time_sup.r.o.get_time != get_not_corrected_time) late_init_time_correction(); #endif erts_late_sys_init_time(); @@ -1038,9 +1218,9 @@ ErtsTimeOffsetState erts_time_offset_state(void) case ERTS_NO_TIME_WARP_MODE: return ERTS_TIME_OFFSET_FINAL; case ERTS_SINGLE_TIME_WARP_MODE: - if (time_sup.inf.c.finalized_offset) - return ERTS_TIME_OFFSET_FINAL; - return ERTS_TIME_OFFSET_PRELIMINARY; + if (erts_smp_atomic32_read_nob(&time_sup.inf.c.preliminary_offset)) + return ERTS_TIME_OFFSET_PRELIMINARY; + return ERTS_TIME_OFFSET_FINAL; case ERTS_MULTI_TIME_WARP_MODE: return ERTS_TIME_OFFSET_VOLATILE; default: @@ -1073,7 +1253,7 @@ erts_finalize_time_offset(void) erts_smp_mtx_lock(&erts_get_time_mtx); - if (!time_sup.inf.c.finalized_offset) { + if (erts_smp_atomic32_read_nob(&time_sup.inf.c.preliminary_offset)) { ErtsMonotonicTime mtime, new_offset; #ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT @@ -1110,19 +1290,12 @@ erts_finalize_time_offset(void) set_time_offset(new_offset); schedule_send_time_offset_changed_notifications(new_offset); - time_sup.inf.c.finalized_offset = ~0; + erts_smp_atomic32_set_nob(&time_sup.inf.c.preliminary_offset, 0); res = ERTS_TIME_OFFSET_PRELIMINARY; } erts_smp_mtx_unlock(&erts_get_time_mtx); -#ifdef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT - if (res == ERTS_TIME_OFFSET_PRELIMINARY - && time_sup.r.o.get_time == get_corrected_time) { - late_init_time_correction(); - } -#endif - return res; } default: diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 251b39508f..f7a21406f3 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -703,14 +703,9 @@ extern char *erts_default_arg0; extern char os_type[]; -typedef enum { - ERTS_NO_TIME_WARP_MODE, - ERTS_SINGLE_TIME_WARP_MODE, - ERTS_MULTI_TIME_WARP_MODE -} ErtsTimeWarpMode; - typedef struct { int have_os_monotonic_time; + int have_corrected_os_monotonic_time; ErtsMonotonicTime os_monotonic_time_unit; ErtsMonotonicTime sys_clock_resolution; struct { @@ -729,14 +724,13 @@ typedef struct { } ErtsSysInitTimeResult; #define ERTS_SYS_INIT_TIME_RESULT_INITER \ - {0, (ErtsMonotonicTime) -1, (ErtsMonotonicTime) 1} + {0, 0, (ErtsMonotonicTime) -1, (ErtsMonotonicTime) 1} 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); diff --git a/erts/emulator/beam/time.c b/erts/emulator/beam/time.c index 2bdda6c8af..1fffb5f357 100644 --- a/erts/emulator/beam/time.c +++ b/erts/emulator/beam/time.c @@ -115,7 +115,7 @@ struct ErtsTimerWheel_ { Uint nto; struct { ErlTimer *head; - ErlTimer **tail; + ErlTimer *tail; Uint nto; } at_once; int true_next_timeout_time; @@ -235,23 +235,39 @@ found_next: static void remove_timer(ErtsTimerWheel *tiw, ErlTimer *p) { - /* first */ - if (!p->prev) { - tiw->w[p->slot] = p->next; + if (p->slot >= 0) { + /* Timer in wheel... */ + ASSERT(p->slot < TIW_SIZE); + if (p->prev) + p->prev->next = p->next; + else { + ASSERT(tiw->w[p->slot] == p); + tiw->w[p->slot] = p->next; + } if(p->next) - p->next->prev = NULL; - } else { - p->prev->next = p->next; + p->next->prev = p->prev; } - - /* last */ - if (!p->next) { + else { + /* Timer in "at once" queue... */ + ASSERT(p->slot == -1); if (p->prev) - p->prev->next = NULL; - } else { - p->next->prev = p->prev; + p->prev->next = p->next; + else { + ASSERT(tiw->at_once.head == p); + tiw->at_once.head = p->next; + } + if (p->next) + p->next->prev = p->prev; + else { + ASSERT(tiw->at_once.tail == p); + tiw->at_once.tail = p->prev; + } + ASSERT(tiw->at_once.nto > 0); + tiw->at_once.nto--; } + p->slot = -2; + p->next = NULL; p->prev = NULL; @@ -337,11 +353,17 @@ erts_bump_timers(ErtsTimerWheel *tiw, ErtsMonotonicTime curr_time) } else { ASSERT(tiw->nto >= tiw->at_once.nto); - timeout_head = tiw->at_once.head; - timeout_tail = tiw->at_once.tail; + p = timeout_head = tiw->at_once.head; + while (1) { + set_timer_wheel(p, NULL); + if (!p->next) { + timeout_tail = &p->next; + break; + } + } tiw->nto -= tiw->at_once.nto; tiw->at_once.head = NULL; - tiw->at_once.tail = &tiw->at_once.head; + tiw->at_once.tail = NULL; tiw->at_once.nto = 0; } @@ -458,7 +480,7 @@ erts_create_timer_wheel(int no) tiw->pos = ERTS_MONOTONIC_TO_CLKTCKS(mtime); tiw->nto = 0; tiw->at_once.head = NULL; - tiw->at_once.tail = &tiw->at_once.head; + tiw->at_once.tail = NULL; tiw->at_once.nto = 0; tiw->true_next_timeout_time = 0; tiw->next_timeout_time = mtime + ERTS_MONOTONIC_DAY; @@ -523,10 +545,13 @@ erts_set_timer(ErlTimer *p, ErlTimeoutProc timeout, timeout_pos = ERTS_MONOTONIC_TO_CLKTCKS(curr_time); tiw->nto++; tiw->at_once.nto++; - *tiw->at_once.tail = p; - tiw->at_once.tail = &p->next; p->next = NULL; + p->prev = tiw->at_once.tail; + tiw->at_once.tail = p; + if (!tiw->at_once.head) + tiw->at_once.head = p; p->timeout_pos = timeout_pos; + p->slot = -1; timeout_time = ERTS_CLKTCKS_TO_MONOTONIC(timeout_pos); } else { @@ -538,7 +563,7 @@ erts_set_timer(ErlTimer *p, ErlTimeoutProc timeout, /* calculate slot */ tm = (int) (timeout_pos & (TIW_SIZE-1)); - p->slot = (Uint) tm; + p->slot = tm; /* insert at head of list at slot */ p->next = tiw->w[tm]; @@ -587,7 +612,6 @@ erts_cancel_timer(ErlTimer *p) cancel = NULL; else { remove_timer(tiw, p); - p->slot = 0; cancel = p->cancel; arg = p->arg; diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 8fc5a3ca49..29a65f883c 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -177,10 +177,10 @@ typedef ErtsMonotonicTime ErtsSystemTime; /* * OS monotonic time and OS system time */ - #undef ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ -#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) \ + && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) # if defined(__linux__) # define ERTS_OS_TIMES_INLINE_FUNC_PTR_CALL__ 1 # endif @@ -191,13 +191,11 @@ ErtsSystemTime erts_os_system_time(void); #undef ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT #undef ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT #undef ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ -#undef ERTS_HAVE_CORRECTED_OS_MONOTONIC #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) # define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 # define ERTS_COMPILE_TIME_MONOTONIC_TIME_UNIT (1000*1000*1000) # if defined(__linux__) -# define ERTS_HAVE_CORRECTED_OS_MONOTONIC 1 # define ERTS_OS_MONOTONIC_INLINE_FUNC_PTR_CALL__ 1 # endif #elif defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) diff --git a/erts/emulator/sys/unix/sys_time.c b/erts/emulator/sys/unix/sys_time.c index d535457977..dc1822b21c 100644 --- a/erts/emulator/sys/unix/sys_time.c +++ b/erts/emulator/sys/unix/sys_time.c @@ -31,6 +31,7 @@ # undef _FILE_OFFSET_BITS #endif +#include <stdlib.h> #include "sys.h" #include "global.h" #include "erl_os_monotonic_time_extender.h" @@ -39,14 +40,11 @@ #undef ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ #if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ - || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) -# include <mach/clock.h> -# include <mach/mach.h> -# ifdef HAVE_CLOCK_GET_ATTRIBUTES -# define ERTS_HAVE_MACH_CLOCK_GETRES -static Sint64 -mach_clock_getres(clock_id_t clkid, char *clkid_str); -# endif + || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) \ + || defined(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME) +# include <mach/clock.h> +# include <mach/mach.h> +# define ERTS_MACH_CLOCKS #endif #ifdef NO_SYSCONF @@ -99,20 +97,53 @@ ErtsSysTimeData__ erts_sys_time_data__ erts_align_attribute(ERTS_CACHE_LINE_SIZE #define ERTS_SYS_TIME_INTERNAL_STATE_WRITE_FREQ__ -static ErtsMonotonicTime clock_gettime_monotonic_raw(void); +static ErtsMonotonicTime clock_gettime_monotonic(void); static ErtsMonotonicTime clock_gettime_monotonic_verified(void); +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) +static ErtsMonotonicTime clock_gettime_monotonic_raw(void); +#endif #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) -static void clock_gettime_times_raw(ErtsMonotonicTime *, ErtsSystemTime *); +static void clock_gettime_times(ErtsMonotonicTime *, ErtsSystemTime *); static void clock_gettime_times_verified(ErtsMonotonicTime *, ErtsSystemTime *); +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) +static void clock_gettime_times_raw(ErtsMonotonicTime *, ErtsSystemTime *); +#endif #endif #endif /* defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ +#ifdef ERTS_MACH_CLOCKS +# define ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ +typedef struct { + clock_id_t id; + clock_serv_t srv; + char *name; +} ErtsMachClock; + +typedef struct { + host_name_port_t host; + struct { + ErtsMachClock monotonic; + ErtsMachClock wall; + } clock; +} ErtsMachClocks; +static void mach_clocks_init(void); +static void mach_clocks_fini(void); +# ifdef HAVE_CLOCK_GET_ATTRIBUTES +# define ERTS_HAVE_MACH_CLOCK_GETRES +static Sint64 +mach_clock_getres(ErtsMachClock *clk); +# endif +#endif /* ERTS_MACH_CLOCKS */ + #ifdef ERTS_SYS_TIME_INTERNAL_STATE_READ_ONLY__ struct sys_time_internal_state_read_only__ { #if defined(OS_MONOTONIC_TIME_USING_TIMES) int times_shift; #endif +#ifdef ERTS_MACH_CLOCKS + ErtsMachClocks mach; +#endif }; #endif @@ -166,13 +197,23 @@ static struct { void sys_init_time(ErtsSysInitTimeResult *init_resp) { +#if defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) + int major, minor, build, vsn; +#endif +#if defined(ERTS_MACH_CLOCKS) + mach_clocks_init(); +#endif #if !defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) init_resp->have_os_monotonic_time = 0; #else /* defined(ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT) */ - int major, minor, build, vsn; +#ifdef ERTS_HAVE_CORRECTED_OS_MONOTONIC_TIME + init_resp->have_corrected_os_monotonic_time = 1; +#else + init_resp->have_corrected_os_monotonic_time = 0; +#endif init_resp->os_monotonic_time_info.resolution = (Uint64) 1000*1000*1000; #if defined(HAVE_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) @@ -187,7 +228,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) } #elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(MONOTONIC_CLOCK_ID) init_resp->os_monotonic_time_info.resolution - = mach_clock_getres(MONOTONIC_CLOCK_ID, MONOTONIC_CLOCK_ID_STR); + = mach_clock_getres(&internal_state.r.o.mach.clock.monotonic); #endif #ifdef MONOTONIC_CLOCK_ID_STR @@ -220,29 +261,52 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) #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; + clock_gettime_monotonic; #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) erts_sys_time_data__.r.o.os_times = - clock_gettime_times_raw; + clock_gettime_times; #endif } else { /* * Linux versions prior to 2.6.33 have a - * known bug that sometimes cause monotonic - * time to take small steps backwards. + * known bug that sometimes cause the NTP + * adjusted monotonic clock to take small + * steps backwards. Use raw monotonic clock + * if it is present; otherwise, fall back + * on locked verification of values. */ - erts_sys_time_data__.r.o.os_monotonic_time = - clock_gettime_monotonic_verified; + init_resp->have_corrected_os_monotonic_time = 0; +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) + /* We know that CLOCK_MONOTONIC_RAW is defined, + but we don't know if we got a kernel that + supports it. Support for CLOCK_MONOTONIC_RAW + appeared in kernel 2.6.28... */ + if (vsn >= ERTS_MK_VSN_INT(2, 6, 28)) { + erts_sys_time_data__.r.o.os_monotonic_time = + clock_gettime_monotonic_raw; #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) - erts_sys_time_data__.r.o.os_times = - clock_gettime_times_verified; + erts_sys_time_data__.r.o.os_times = + clock_gettime_times_raw; #endif - 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_time_info.locked_use = 1; + init_resp->os_monotonic_time_info.clock_id = + "CLOCK_MONOTONIC_RAW"; + } + else +#endif /* defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) */ + { + erts_sys_time_data__.r.o.os_monotonic_time = + clock_gettime_monotonic_verified; +#if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) + erts_sys_time_data__.r.o.os_times = + clock_gettime_times_verified; +#endif + erts_smp_mtx_init(&internal_state.w.f.mtx, + "os_monotonic_time"); + internal_state.w.f.last_delivered + = clock_gettime_monotonic(); + init_resp->os_monotonic_time_info.locked_use = 1; + } } #else /* !(defined(__linux__) && defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)) */ { @@ -324,7 +388,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) } #elif defined(ERTS_HAVE_MACH_CLOCK_GETRES) && defined(WALL_CLOCK_ID) init_resp->os_system_time_info.resolution - = mach_clock_getres(WALL_CLOCK_ID, WALL_CLOCK_ID_STR); + = mach_clock_getres(&internal_state.r.o.mach.clock.wall); #endif #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) @@ -366,6 +430,10 @@ adj_stime_time_unit(ErtsSystemTime stime, Uint32 res) (Uint32) ERTS_MONOTONIC_TIME_UNIT)); } +#define ERTS_TimeSpec2Sint64(TS) \ + ((((Sint64) (TS)->tv_sec) * ((Sint64) 1000*1000*1000)) \ + + ((Sint64) (TS)->tv_nsec)) + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * POSIX clock_gettime() * \* */ @@ -373,17 +441,7 @@ adj_stime_time_unit(ErtsSystemTime stime, Uint32 res) #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) \ || defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) -static ERTS_INLINE ErtsMonotonicTime -timespec2montime(struct timespec *ts) -{ - ErtsMonotonicTime time; - time = (ErtsMonotonicTime) ts->tv_sec; - time *= (ErtsMonotonicTime) 1000*1000*1000; - time += (ErtsMonotonicTime) ts->tv_nsec; - return time; -} - -static ERTS_INLINE ErtsMonotonicTime +static ERTS_INLINE Sint64 posix_clock_gettime(clockid_t id, char *name) { struct timespec ts; @@ -395,7 +453,7 @@ posix_clock_gettime(clockid_t id, char *name) "clock_gettime(%s, _) failed: %s (%d)\n", name, errstr, err); } - return timespec2montime(&ts); + return ERTS_TimeSpec2Sint64(&ts); } #endif /* defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) \ @@ -406,11 +464,10 @@ posix_clock_gettime(clockid_t id, char *name) ErtsSystemTime erts_os_system_time(void) { - ErtsSystemTime stime; - - stime = (ErtsSystemTime) posix_clock_gettime(WALL_CLOCK_ID, - WALL_CLOCK_ID_STR); - return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); + Sint64 stime = posix_clock_gettime(WALL_CLOCK_ID, + WALL_CLOCK_ID_STR); + return adj_stime_time_unit((ErtsSystemTime) stime, + (Uint32) 1000*1000*1000); } #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ @@ -422,32 +479,34 @@ erts_os_system_time(void) #define ERTS_HAVE_ERTS_OS_TIMES_IMPL__ static ERTS_INLINE void -posix_clock_gettime_times(ErtsMonotonicTime *mtimep, +posix_clock_gettime_times(clockid_t mid, char *mname, + ErtsMonotonicTime *mtimep, + clockid_t sid, char *sname, ErtsSystemTime *stimep) { struct timespec mts, sts; int mres, sres, merr, serr; - mres = clock_gettime(MONOTONIC_CLOCK_ID, &mts); + mres = clock_gettime(mid, &mts); merr = errno; - sres = clock_gettime(WALL_CLOCK_ID, &sts); + sres = clock_gettime(sid, &sts); serr = errno; if (mres != 0) { char *errstr = merr ? strerror(merr) : "unknown"; erl_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", - MONOTONIC_CLOCK_ID_STR, errstr, merr); + mname, errstr, merr); } if (sres != 0) { char *errstr = serr ? strerror(serr) : "unknown"; erl_exit(ERTS_ABORT_EXIT, "clock_gettime(%s, _) failed: %s (%d)\n", - WALL_CLOCK_ID_STR, errstr, serr); + sname, errstr, serr); } - *mtimep = timespec2montime(&mts); - *stimep = (ErtsSystemTime) timespec2montime(&sts); + *mtimep = (ErtsMonotonicTime) ERTS_TimeSpec2Sint64(&mts); + *stimep = (ErtsSystemTime) ERTS_TimeSpec2Sint64(&sts); } #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ @@ -456,8 +515,10 @@ posix_clock_gettime_times(ErtsMonotonicTime *mtimep, static ErtsMonotonicTime clock_gettime_monotonic_verified(void) { - ErtsMonotonicTime mtime = posix_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); + ErtsMonotonicTime mtime; + + mtime = (ErtsMonotonicTime) posix_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); erts_smp_mtx_lock(&internal_state.w.f.mtx); if (mtime < internal_state.w.f.last_delivered) @@ -474,7 +535,12 @@ static ErtsMonotonicTime clock_gettime_monotonic_verified(void) static void clock_gettime_times_verified(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - posix_clock_gettime_times(mtimep, stimep); + posix_clock_gettime_times(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR, + mtimep, + WALL_CLOCK_ID, + WALL_CLOCK_ID_STR, + stimep); erts_smp_mtx_lock(&internal_state.w.f.mtx); if (*mtimep < internal_state.w.f.last_delivered) @@ -486,20 +552,50 @@ static void clock_gettime_times_verified(ErtsMonotonicTime *mtimep, #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ +static ErtsMonotonicTime clock_gettime_monotonic(void) +{ + return (ErtsMonotonicTime) posix_clock_gettime(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR); +} + +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) + static ErtsMonotonicTime clock_gettime_monotonic_raw(void) { - return posix_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); + return (ErtsMonotonicTime) posix_clock_gettime(CLOCK_MONOTONIC_RAW, + "CLOCK_MONOTONIC_RAW"); } +#endif /* defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) */ + #if defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) +static void clock_gettime_times(ErtsMonotonicTime *mtimep, + ErtsSystemTime *stimep) +{ + posix_clock_gettime_times(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR, + mtimep, + WALL_CLOCK_ID, + WALL_CLOCK_ID_STR, + stimep); +} + +#if defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) + static void clock_gettime_times_raw(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - posix_clock_gettime_times(mtimep, stimep); + posix_clock_gettime_times(CLOCK_MONOTONIC_RAW, + "CLOCK_MONOTONIC_RAW", + mtimep, + WALL_CLOCK_ID, + WALL_CLOCK_ID_STR, + stimep); } +#endif /* defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) */ + #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ #else /* !defined(__linux__) */ @@ -514,61 +610,116 @@ ErtsMonotonicTime erts_os_monotonic_time(void) void erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - posix_clock_gettime_times(mtimep, stimep); + posix_clock_gettime_times(MONOTONIC_CLOCK_ID, + MONOTONIC_CLOCK_ID_STR, + mtimep, + WALL_CLOCK_ID, + WALL_CLOCK_ID_STR, + stimep); } #endif /* defined(OS_SYSTEM_TIME_USING_CLOCK_GETTIME) */ #endif /* !defined(__linux__) */ -#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ +#endif /* defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ + +#if defined(SYS_HRTIME_USING_CLOCK_GETTIME) +# define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ ErtsSysHrTime erts_sys_hrtime(void) { - return (ErtsSysHrTime) posix_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); + return (ErtsSysHrTime) posix_clock_gettime(HRTIME_CLOCK_ID, + HRTIME_CLOCK_ID_STR); } -#endif /* defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME) */ +#endif /* defined(SYS_HRTIME_USING_CLOCK_GETTIME) */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * MACH clock_get_time() * \* */ -#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ - || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) +#if defined(ERTS_MACH_CLOCKS) -#ifdef ERTS_HAVE_MACH_CLOCK_GETRES +static void +mach_clocks_fini(void) +{ + mach_port_t task = mach_task_self(); + mach_port_deallocate(task, internal_state.r.o.mach.host); +#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) + mach_port_deallocate(task, internal_state.r.o.mach.clock.monotonic.srv); +#endif +#if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + mach_port_deallocate(task, internal_state.r.o.mach.clock.wall.srv); +#endif +} -static Sint64 -mach_clock_getres(clock_id_t clkid, char *clkid_str) +static void +mach_clocks_init(void) { - mach_port_t task; - host_name_port_t host; - natural_t attr[1]; kern_return_t kret; - clock_serv_t clk_srv; - mach_msg_type_number_t cnt; + host_name_port_t host; + clock_id_t id; + clock_serv_t *clck_srv_p; + char *name; + + host = internal_state.r.o.mach.host = mach_host_self(); - host = mach_host_self(); - kret = host_get_clock_service(host, clkid, &clk_srv); +#if defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ + || defined(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME) + id = internal_state.r.o.mach.clock.monotonic.id = MONOTONIC_CLOCK_ID; + name = internal_state.r.o.mach.clock.monotonic.name = MONOTONIC_CLOCK_ID_STR; + clck_srv_p = &internal_state.r.o.mach.clock.monotonic.srv; + kret = host_get_clock_service(host, id, clck_srv_p); if (kret != KERN_SUCCESS) { erl_exit(ERTS_ABORT_EXIT, "host_get_clock_service(_, %s, _) failed\n", - clkid_str); + name); } +#endif - cnt = sizeof(attr); - kret = clock_get_attributes(clk_srv, CLOCK_GET_TIME_RES, (clock_attr_t) attr, &cnt); +#if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) + id = internal_state.r.o.mach.clock.wall.id = WALL_CLOCK_ID; + name = internal_state.r.o.mach.clock.wall.name = WALL_CLOCK_ID_STR; + clck_srv_p = &internal_state.r.o.mach.clock.wall.srv; + kret = host_get_clock_service(host, id, clck_srv_p); if (kret != KERN_SUCCESS) { + erl_exit(ERTS_ABORT_EXIT, + "host_get_clock_service(_, %s, _) failed\n", + name); + } +#endif + + if (atexit(mach_clocks_fini) != 0) { + int err = errno; + char *errstr = err ? strerror(err) : "unknown"; + erl_exit(ERTS_ABORT_EXIT, + "Failed to register mach_clocks_fini() " + "for call at exit: %s (%d)\n", + errstr, err); + } +} + +#ifdef ERTS_HAVE_MACH_CLOCK_GETRES + +static Sint64 +mach_clock_getres(ErtsMachClock *clk) +{ + kern_return_t kret; + natural_t attr[1]; + mach_msg_type_number_t cnt; + + cnt = sizeof(attr); + kret = clock_get_attributes(clk->srv, + CLOCK_GET_TIME_RES, + (clock_attr_t) attr, + &cnt); + if (kret != KERN_SUCCESS || cnt != 1) { erl_exit(ERTS_ABORT_EXIT, "clock_get_attributes(%s, _) failed\n", - clkid_str); + clk->name); } - task = mach_task_self(); - mach_port_deallocate(task, host); - mach_port_deallocate(task, clk_srv); return (Sint64) attr[0]; } @@ -576,41 +727,19 @@ mach_clock_getres(clock_id_t clkid, char *clkid_str) #endif /* ERTS_HAVE_MACH_CLOCK_GETRES */ static ERTS_INLINE Sint64 -mach_clock_gettime(clock_id_t clkid, char *clkid_str) +mach_clock_get_time(ErtsMachClock *clk) { - Sint64 time; - mach_port_t task; - host_name_port_t host; kern_return_t kret; - clock_serv_t clk_srv; mach_timespec_t time_spec; - host = mach_host_self(); - kret = host_get_clock_service(host, clkid, &clk_srv); - if (kret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, - "host_get_clock_service(_, %s, _) failed\n", - clkid_str); - } - errno = 0; - kret = clock_get_time(clk_srv, &time_spec); - if (kret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, - "clock_get_time(%s, _) failed\n", - clkid_str); - } - task = mach_task_self(); - mach_port_deallocate(task, host); - mach_port_deallocate(task, clk_srv); + kret = clock_get_time(clk->srv, &time_spec); + if (kret != KERN_SUCCESS) + erl_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", clk->name); - time = (Sint64) time_spec.tv_sec; - time *= (Sint64) 1000*1000*1000; - time += (Sint64) time_spec.tv_nsec; - return time; + return ERTS_TimeSpec2Sint64(&time_spec); } -#endif /* defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) \ - || defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ +#endif /* defined(ERTS_MACH_CLOCKS) */ #if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) @@ -619,10 +748,9 @@ mach_clock_gettime(clock_id_t clkid, char *clkid_str) ErtsSystemTime erts_os_system_time(void) { - ErtsSystemTime stime; - stime = (ErtsSystemTime) mach_clock_gettime(WALL_CLOCK_ID, - WALL_CLOCK_ID_STR); - return adj_stime_time_unit(stime, (Uint32) 1000*1000*1000); + Sint64 stime = mach_clock_get_time(&internal_state.r.o.mach.clock.wall); + return adj_stime_time_unit((ErtsSystemTime) stime, + (Uint32) 1000*1000*1000); } #endif /* defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ @@ -632,17 +760,8 @@ erts_os_system_time(void) ErtsMonotonicTime erts_os_monotonic_time(void) { - return (ErtsMonotonicTime) mach_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); -} - -#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ - -ErtsSysHrTime -erts_sys_hrtime(void) -{ - return (ErtsMonotonicTime) mach_clock_gettime(MONOTONIC_CLOCK_ID, - MONOTONIC_CLOCK_ID_STR); + return (ErtsMonotonicTime) + mach_clock_get_time(&internal_state.r.o.mach.clock.monotonic); } #if defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) @@ -652,58 +771,44 @@ erts_sys_hrtime(void) void erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - ErtsMonotonicTime mtime; - ErtsSystemTime stime; - mach_port_t task; - host_name_port_t host; kern_return_t mkret, skret; - clock_serv_t mclk_srv, sclk_srv; mach_timespec_t mon_time_spec, sys_time_spec; - host = mach_host_self(); - mkret = host_get_clock_service(host, MONOTONIC_CLOCK_ID, &mclk_srv); - skret = host_get_clock_service(host, WALL_CLOCK_ID, &sclk_srv); - if (mkret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, - "host_get_clock_service(_, %s, _) failed\n", - MONOTONIC_CLOCK_ID); - } - if (skret != KERN_SUCCESS) { - erl_exit(ERTS_ABORT_EXIT, - "host_get_clock_service(_, %s, _) failed\n", - WALL_CLOCK_ID); - } - mkret = clock_get_time(mclk_srv, &mon_time_spec); - skret = clock_get_time(sclk_srv, &sys_time_spec); - if (mkret != KERN_SUCCESS) { + mkret = clock_get_time(internal_state.r.o.mach.clock.monotonic.srv, + &mon_time_spec); + skret = clock_get_time(internal_state.r.o.mach.clock.wall.srv, + &sys_time_spec); + + if (mkret != KERN_SUCCESS) erl_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", - MONOTONIC_CLOCK_ID); - } - if (skret != KERN_SUCCESS) { + internal_state.r.o.mach.clock.monotonic.name); + if (skret != KERN_SUCCESS) erl_exit(ERTS_ABORT_EXIT, "clock_get_time(%s, _) failed\n", - WALL_CLOCK_ID); - } - task = mach_task_self(); - mach_port_deallocate(task, host); - mach_port_deallocate(task, mclk_srv); - mach_port_deallocate(task, sclk_srv); + internal_state.r.o.mach.clock.wall.name); - mtime = (ErtsMonotonicTime) mon_time_spec.tv_sec; - mtime *= (ErtsMonotonicTime) 1000*1000*1000; - mtime += (ErtsMonotonicTime) mon_time_spec.tv_nsec; - stime = (ErtsSystemTime) sys_time_spec.tv_sec; - stime *= (ErtsSystemTime) 1000*1000*1000; - stime += (ErtsSystemTime) sys_time_spec.tv_nsec; - *mtimep = mtime; - *stimep = stime; + *mtimep = (ErtsMonotonicTime) ERTS_TimeSpec2Sint64(&mon_time_spec); + *stimep = (ErtsSystemTime) ERTS_TimeSpec2Sint64(&sys_time_spec); } #endif /* defined(OS_SYSTEM_TIME_USING_MACH_CLOCK_GET_TIME) */ #endif /* defined(OS_MONOTONIC_TIME_USING_MACH_CLOCK_GET_TIME) */ +#if defined(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME) + +#define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ + +ErtsSysHrTime +erts_sys_hrtime(void) +{ + return (ErtsSysHrTime) + mach_clock_get_time(&internal_state.r.o.mach.clock.monotonic); +} + +#endif /* defined(SYS_HRTIME_USING_MACH_CLOCK_GET_TIME) */ + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Solaris gethrtime() - OS monotonic time * \* */ @@ -715,6 +820,10 @@ ErtsMonotonicTime erts_os_monotonic_time(void) return (ErtsMonotonicTime) gethrtime(); } +#endif /* defined(OS_MONOTONIC_TIME_USING_GETHRTIME) */ + +#if defined(SYS_HRTIME_USING_GETHRTIME) + #define ERTS_HAVE_ERTS_SYS_HRTIME_IMPL__ ErtsSysHrTime @@ -723,7 +832,7 @@ erts_sys_hrtime(void) return (ErtsSysHrTime) gethrtime(); } -#endif /* defined(OS_MONOTONIC_TIME_USING_GETHRTIME) */ +#endif /* defined(SYS_HRTIME_USING_GETHRTIME) */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * gettimeofday() - OS system time * diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 5a62b00a68..937a678702 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -25,6 +25,7 @@ #include "sys.h" #include "erl_alloc.h" #include "erl_poll.h" +#include "erl_time.h" /* * Some debug macros diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 5181d6b584..9ddc0fbb41 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -224,7 +224,7 @@ erts_os_monotonic_time(void) ERTS_GLB_INLINE void erts_os_times(ErtsMonotonicTime *mtimep, ErtsSystemTime *stimep) { - return (*erts_sys_time_data__.r.o.os_times)(mtimep, stimep); + (*erts_sys_time_data__.r.o.os_times)(mtimep, stimep); } ERTS_GLB_INLINE ErtsSysHrTime diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c index b292d9279e..7da060a7a7 100644 --- a/erts/emulator/sys/win32/sys_time.c +++ b/erts/emulator/sys/win32/sys_time.c @@ -28,6 +28,9 @@ #include "erl_os_monotonic_time_extender.h" #include "erl_time.h" +/* Need to look more closely at qpc before use... */ +#define ERTS_DISABLE_USE_OF_QPC_FOR_MONOTONIC_TIME 1 + #define LL_LITERAL(X) ERTS_I64_LITERAL(X) /******************* Routines for time measurement *********************/ @@ -362,10 +365,11 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) if (!internal_state.r.o.pQueryPerformanceCounter) goto get_tick_count64; - if (pf.QuadPart < (((LONGLONG) 1) << 32)) { - internal_state.r.o.pcf = (Uint32) pf.QuadPart; - sys_hrtime_func = sys_hrtime_qpc; - } + if (pf.QuadPart > (((LONGLONG) 1) << 32)) + goto get_tick_count64; + + internal_state.r.o.pcf = (Uint32) pf.QuadPart; + sys_hrtime_func = sys_hrtime_qpc; /* * We only use QueryPerformanceCounter() for @@ -377,6 +381,9 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) if (pf.QuadPart < (LONGLONG) 1000*1000*1000) goto get_tick_count64; + if (ERTS_DISABLE_USE_OF_QPC_FOR_MONOTONIC_TIME) + 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; @@ -391,6 +398,7 @@ sys_init_time(ErtsSysInitTimeResult *init_resp) erts_sys_time_data__.r.o.os_times = os_times_func; init_resp->os_monotonic_time_unit = time_unit; init_resp->have_os_monotonic_time = 1; + init_resp->have_corrected_os_monotonic_time = 0; init_resp->sys_clock_resolution = 1; init_resp->os_system_time_info.func = "GetSystemTime"; |