diff options
Diffstat (limited to 'erts/emulator/beam/erl_time_sup.c')
-rw-r--r-- | erts/emulator/beam/erl_time_sup.c | 899 |
1 files changed, 899 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c new file mode 100644 index 0000000000..76bfdecd9f --- /dev/null +++ b/erts/emulator/beam/erl_time_sup.c @@ -0,0 +1,899 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1999-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* +** Support routines for the timer wheel +** +** This code contains two strategies for dealing with +** date/time changes in the system. +** If the system has some kind of high resolution timer (HAVE_GETHRTIME), +** the high resolution timer is used to correct the time-of-day and the +** timeouts, the base source is the hrtimer, but at certain intervals the +** OS time-of-day is checked and if it is not within certain bounds, the +** delivered time gets slowly adjusted for each call until +** it corresponds to the system time (built-in adjtime...). +** The call gethrtime() is detected by autoconf on Unix, but other +** platforms may define it in erl_*_sys.h and implement +** their own high resolution timer. The high resolution timer +** strategy is (probably) best on all systems where the timer have +** a resolution higher or equal to gettimeofday (or what's implemented +** is sys_gettimeofday()). The actual resolution is the interesting thing, +** not the unit's thats used (i.e. on VxWorks, nanoseconds can be +** retrieved in terms of units, but the actual resolution is the same as +** for the clock ticks). +** If the systems best timer routine is kernel ticks returned from +** sys_times(), and the actual resolution of sys_gettimeofday() is +** better (like most unixes that does not have any realtime extensions), +** another strategy is used. The tolerant gettimeofday() corrects +** the value with respect to uptime (sys_times() return value) and checks +** for correction both when delivering timeticks and delivering nowtime. +** this strategy is slower, but accurate on systems without better timer +** routines. The kernel tick resolution is not enough to implement +** a gethrtime routine. On Linux and other non solaris unix-boxes the second +** strategy is used, on all other platforms we use the first. +** +** The following is expected (from sys.[ch] and erl_*_sys.h): +** +** 64 bit integers. So it is, and so it will be. +** +** sys_init_time(), will return the clock resolution in MS and +** that's about it. More could be added of course +** If the clock-rate is constant (i.e. 1 ms) one can define +** SYS_CLOCK_RESOLUTION (to 1), +** which makes erts_deliver_time/erts_time_remaining a bit faster. +** +** if HAVE_GETHRTIME is defined: +** sys_gethrtime() will return a SysHrTime (long long) representing +** nanoseconds, sys_init_hrtime() will do any initialization. +** else +** a long (64bit) integer type called Sint64 should be defined. +** +** sys_times() will return clock_ticks since start and +** fill in a SysTimes structure (struct tms). Instead of CLK_TCK, +** SYS_CLK_TCK is used to determine the resolution of kernel ticks. +** +** sys_gettimeofday() will take a SysTimeval (a struct timeval) as parameter +** and fill it in as gettimeofday(X,NULL). +** +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_vm.h" +#include "global.h" + +static erts_smp_mtx_t erts_timeofday_mtx; + +static SysTimeval inittv; /* Used everywhere, the initial time-of-day */ + +static SysTimes t_start; /* Used in elapsed_time_both */ +static SysTimeval gtv; /* Used in wall_clock_elapsed_time_both */ +static SysTimeval then; /* Used in get_now */ +static SysTimeval last_emu_time; /* Used in erts_get_emu_time() */ +SysTimeval erts_first_emu_time; /* Used in erts_get_emu_time() */ + + +#ifdef HAVE_GETHRTIME + +int erts_disable_tolerant_timeofday; + +static SysHrTime hr_init_time, hr_last_correction_check, + hr_correction, hr_last_time; + +static void init_tolerant_timeofday(void) +{ + /* Should be in sys.c */ +#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) + if (sysconf(_SC_NPROCESSORS_CONF) > 1) { + char b[1024]; + int maj,min,build; + os_flavor(b,1024); + os_version(&maj,&min,&build); + if (!strcmp(b,"sunos") && maj <= 5 && min <= 7) { + erts_disable_tolerant_timeofday = 1; + } + } +#endif + hr_init_time = sys_gethrtime(); + hr_last_correction_check = hr_last_time = hr_init_time; + hr_correction = 0; +} + +static void get_tolerant_timeofday(SysTimeval *tv) +{ + SysHrTime diff_time, curr; + + if (erts_disable_tolerant_timeofday) { + sys_gettimeofday(tv); + return; + } + *tv = inittv; + diff_time = ((curr = sys_gethrtime()) + hr_correction - hr_init_time) / 1000; + + if (curr < hr_init_time) { + erl_exit(1,"Unexpected behaviour from operating system high " + "resolution timer"); + } + + if ((curr - hr_last_correction_check) / 1000 > 1000000) { + /* Check the correction need */ + SysHrTime tv_diff, diffdiff; + SysTimeval tmp; + int done = 0; + + sys_gettimeofday(&tmp); + tv_diff = ((SysHrTime) tmp.tv_sec) * 1000000 + tmp.tv_usec; + tv_diff -= ((SysHrTime) inittv.tv_sec) * 1000000 + inittv.tv_usec; + diffdiff = diff_time - tv_diff; + if (diffdiff > 10000) { + SysHrTime corr = (curr - hr_last_time) / 100; + if (corr / 1000 >= diffdiff) { + ++done; + hr_correction -= ((SysHrTime)diffdiff) * 1000; + } else { + hr_correction -= corr; + } + diff_time = (curr + hr_correction - hr_init_time) / 1000; + } else if (diffdiff < -10000) { + SysHrTime corr = (curr - hr_last_time) / 100; + if (corr / 1000 >= -diffdiff) { + ++done; + hr_correction -= ((SysHrTime)diffdiff) * 1000; + } else { + hr_correction += corr; + } + diff_time = (curr + hr_correction - hr_init_time) / 1000; + } else { + ++done; + } + if (done) { + hr_last_correction_check = curr; + } + } + tv->tv_sec += (int) (diff_time / ((SysHrTime) 1000000)); + tv->tv_usec += (int) (diff_time % ((SysHrTime) 1000000)); + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec += 1; + } + hr_last_time = curr; +} + +#define correction (hr_correction/1000000) + +#else /* !HAVE_GETHRTIME */ +#if !defined(CORRECT_USING_TIMES) +#define init_tolerant_timeofday() +#define get_tolerant_timeofday(tvp) sys_gettimeofday(tvp) +#else + +typedef Sint64 Milli; + +static clock_t init_ct; +static Sint64 ct_wrap; +static Milli init_tv_m; +static Milli correction_supress; +static Milli last_ct_diff; +static Milli last_cc; +static clock_t last_ct; + +/* 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 + 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 +#define KERNEL_TICKS() (sys_times_wrap() & \ + ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) +#else +SysTimes dummy_tms; + +#define KERNEL_TICKS() (sys_times(&dummy_tms) & \ + ((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) + +#endif + +static void init_tolerant_timeofday(void) +{ + last_ct = init_ct = KERNEL_TICKS(); + last_cc = 0; + init_tv_m = (((Milli) inittv.tv_sec) * 1000) + + (inittv.tv_usec / 1000); + ct_wrap = 0; + correction_supress = 0; +} + + +static void get_tolerant_timeofday(SysTimeval *tvp) +{ + clock_t current_ct; + SysTimeval current_tv; + Milli ct_diff; + Milli tv_diff; + Milli current_correction; + Milli act_correction; /* long shown to be too small */ + Milli max_adjust; + + if (erts_disable_tolerant_timeofday) { + sys_gettimeofday(tvp); + return; + } + +#ifdef ERTS_WRAP_SYS_TIMES +#define TICK_MS (1000 / SYS_CLK_TCK_WRAP) +#else +#define TICK_MS (1000 / SYS_CLK_TCK) +#endif + current_ct = KERNEL_TICKS(); + sys_gettimeofday(¤t_tv); + + /* I dont know if uptime can move some units backwards + on some systems, but I allow for small backward + jumps to avoid such problems if they exist...*/ + if (last_ct > 100 && current_ct < (last_ct - 100)) { + ct_wrap += ((Sint64) 1) << ((sizeof(clock_t) * 8) - 1); + } + last_ct = current_ct; + ct_diff = ((ct_wrap + current_ct) - init_ct) * TICK_MS; + + /* + * We will adjust the time in milliseconds and we allow for 1% + * adjustments, but if this function is called more often then every 100 + * millisecond (which is obviously possible), we will never adjust, so + * we accumulate small times by setting last_ct_diff iff max_adjust > 0 + */ + if ((max_adjust = (ct_diff - last_ct_diff)/100) > 0) + last_ct_diff = ct_diff; + + tv_diff = ((((Milli) current_tv.tv_sec) * 1000) + + (current_tv.tv_usec / 1000)) - init_tv_m; + + current_correction = ((ct_diff - tv_diff) / TICK_MS) * TICK_MS; /* trunc */ + + /* + * We allow the current_correction value to wobble a little, as it + * suffers from the low resolution of the kernel ticks. + * if it hasn't changed more than one tick in either direction, + * we will keep the old value. + */ + if ((last_cc > current_correction + TICK_MS) || + (last_cc < current_correction - TICK_MS)) { + last_cc = current_correction; + } else { + current_correction = last_cc; + } + + /* + * As time goes, we try to get the actual correction to 0, + * that is, make erlangs time correspond to the systems dito. + * The act correction is what we seem to need (current_correction) + * minus the correction suppression. The correction supression + * will change slowly (max 1% of elapsed time) but in millisecond steps. + */ + act_correction = current_correction - correction_supress; + if (max_adjust > 0) { + /* + * Here we slowly adjust erlangs time to correspond with the + * system time by changing the correction_supress variable. + * It can change max_adjust milliseconds which is 1% of elapsed time + */ + if (act_correction > 0) { + if (current_correction - correction_supress > max_adjust) { + correction_supress += max_adjust; + } else { + correction_supress = current_correction; + } + act_correction = current_correction - correction_supress; + } else if (act_correction < 0) { + if (correction_supress - current_correction > max_adjust) { + correction_supress -= max_adjust; + } else { + correction_supress = current_correction; + } + act_correction = current_correction - correction_supress; + } + } + /* + * The actual correction will correct the timeval so that system + * time warps gets smothed down. + */ + current_tv.tv_sec += act_correction / 1000; + current_tv.tv_usec += (act_correction % 1000) * 1000; + + if (current_tv.tv_usec >= 1000000) { + ++current_tv.tv_sec ; + current_tv.tv_usec -= 1000000; + } else if (current_tv.tv_usec < 0) { + --current_tv.tv_sec; + current_tv.tv_usec += 1000000; + } + *tvp = current_tv; +#undef TICK_MS +} + +#endif /* CORRECT_USING_TIMES */ +#endif /* !HAVE_GETHRTIME */ + +/* +** Why this? Well, most platforms have a constant clock resolution of 1, +** we dont want the deliver_time/time_remaining routines to waste +** time dividing and multiplying by/with a variable that's always one. +** so the return value of sys_init_time is ignored on those platforms. +*/ + +#ifndef SYS_CLOCK_RESOLUTION +static int clock_resolution; +#define CLOCK_RESOLUTION clock_resolution +#else +#define CLOCK_RESOLUTION SYS_CLOCK_RESOLUTION +#endif + +/* +** The clock resolution should really be the resolution of the +** time function in use, which on most platforms +** is 1. On VxWorks the resolution shold be +** the number of ticks per second (or 1, which would work nicely to). +** +** Setting lower resolutions is mostly interesting when timers are used +** instead of something like select. +*/ + +#if defined(ERTS_TIMER_THREAD) +static ERTS_INLINE void init_erts_deliver_time(const SysTimeval *inittv) { } +static ERTS_INLINE void do_erts_deliver_time(const SysTimeval *current) { } +#else +static SysTimeval last_delivered; + +static void init_erts_deliver_time(const SysTimeval *inittv) +{ + /* We set the initial values for deliver_time here */ + last_delivered = *inittv; + last_delivered.tv_usec = 1000 * (last_delivered.tv_usec / 1000); + /* ms resolution */ +} + +static void do_erts_deliver_time(const SysTimeval *current) +{ + SysTimeval cur_time; + long elapsed; + + /* calculate and deliver appropriate number of ticks */ + cur_time = *current; + cur_time.tv_usec = 1000 * (cur_time.tv_usec / 1000); /* ms resolution */ + elapsed = (1000 * (cur_time.tv_sec - last_delivered.tv_sec) + + (cur_time.tv_usec - last_delivered.tv_usec) / 1000) / + CLOCK_RESOLUTION; + + /* Sometimes the time jump backwards, + resulting in a negative elapsed time. We compensate for + this by simply pretend as if the time stood still. :) */ + + if (elapsed > 0) { + do_time_add(elapsed); + last_delivered = cur_time; + } +} +#endif + +int +erts_init_time_sup(void) +{ + erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday"); + + last_emu_time.tv_sec = 0; + last_emu_time.tv_usec = 0; + +#ifndef SYS_CLOCK_RESOLUTION + clock_resolution = sys_init_time(); +#else + (void) sys_init_time(); +#endif + sys_gettimeofday(&inittv); + +#ifdef HAVE_GETHRTIME + sys_init_hrtime(); +#endif + init_tolerant_timeofday(); + + init_erts_deliver_time(&inittv); + gtv = inittv; + then.tv_sec = then.tv_usec = 0; + + erts_get_emu_time(&erts_first_emu_time); + + return CLOCK_RESOLUTION; +} +/* info functions */ + +void +elapsed_time_both(unsigned long *ms_user, unsigned long *ms_sys, + unsigned long *ms_user_diff, unsigned long *ms_sys_diff) +{ + unsigned long prev_total_user, prev_total_sys; + unsigned long total_user, total_sys; + SysTimes now; + + sys_times(&now); + total_user = (now.tms_utime * 1000) / SYS_CLK_TCK; + total_sys = (now.tms_stime * 1000) / SYS_CLK_TCK; + + if (ms_user != NULL) + *ms_user = total_user; + if (ms_sys != NULL) + *ms_sys = total_sys; + + erts_smp_mtx_lock(&erts_timeofday_mtx); + + prev_total_user = (t_start.tms_utime * 1000) / SYS_CLK_TCK; + prev_total_sys = (t_start.tms_stime * 1000) / SYS_CLK_TCK; + t_start = now; + + erts_smp_mtx_unlock(&erts_timeofday_mtx); + + if (ms_user_diff != NULL) + *ms_user_diff = total_user - prev_total_user; + + if (ms_sys_diff != NULL) + *ms_sys_diff = total_sys - prev_total_sys; +} + + +/* wall clock routines */ + +void +wall_clock_elapsed_time_both(unsigned long *ms_total, unsigned long *ms_diff) +{ + unsigned long prev_total; + SysTimeval tv; + + erts_smp_mtx_lock(&erts_timeofday_mtx); + + get_tolerant_timeofday(&tv); + + *ms_total = 1000 * (tv.tv_sec - inittv.tv_sec) + + (tv.tv_usec - inittv.tv_usec) / 1000; + + prev_total = 1000 * (gtv.tv_sec - inittv.tv_sec) + + (gtv.tv_usec - inittv.tv_usec) / 1000; + *ms_diff = *ms_total - prev_total; + gtv = tv; + + /* must sync the machine's idea of time here */ + do_erts_deliver_time(&tv); + + erts_smp_mtx_unlock(&erts_timeofday_mtx); +} + +/* get current time */ +void +get_time(int *hour, int *minute, int *second) +{ + time_t the_clock; + struct tm *tm; +#ifdef HAVE_LOCALTIME_R + struct tm tmbuf; +#endif + + the_clock = time((time_t *)0); +#ifdef HAVE_LOCALTIME_R + localtime_r(&the_clock, (tm = &tmbuf)); +#else + tm = localtime(&the_clock); +#endif + *hour = tm->tm_hour; + *minute = tm->tm_min; + *second = tm->tm_sec; +} + +/* get current date */ +void +get_date(int *year, int *month, int *day) +{ + time_t the_clock; + struct tm *tm; +#ifdef HAVE_LOCALTIME_R + struct tm tmbuf; +#endif + + + the_clock = time((time_t *)0); +#ifdef HAVE_LOCALTIME_R + localtime_r(&the_clock, (tm = &tmbuf)); +#else + tm = localtime(&the_clock); +#endif + *year = tm->tm_year + 1900; + *month = tm->tm_mon +1; + *day = tm->tm_mday; +} + +/* get localtime */ +void +get_localtime(int *year, int *month, int *day, + int *hour, int *minute, int *second) +{ + time_t the_clock; + struct tm *tm; +#ifdef HAVE_LOCALTIME_R + struct tm tmbuf; +#endif + + the_clock = time((time_t *)0); +#ifdef HAVE_LOCALTIME_R + localtime_r(&the_clock, (tm = &tmbuf)); +#else + tm = localtime(&the_clock); +#endif + *year = tm->tm_year + 1900; + *month = tm->tm_mon +1; + *day = tm->tm_mday; + *hour = tm->tm_hour; + *minute = tm->tm_min; + *second = tm->tm_sec; +} + + +/* get universaltime */ +void +get_universaltime(int *year, int *month, int *day, + int *hour, int *minute, int *second) +{ + time_t the_clock; + struct tm *tm; +#ifdef HAVE_GMTIME_R + struct tm tmbuf; +#endif + + the_clock = time((time_t *)0); +#ifdef HAVE_GMTIME_R + gmtime_r(&the_clock, (tm = &tmbuf)); +#else + tm = gmtime(&the_clock); +#endif + *year = tm->tm_year + 1900; + *month = tm->tm_mon +1; + *day = tm->tm_mday; + *hour = tm->tm_hour; + *minute = tm->tm_min; + *second = tm->tm_sec; +} + + +/* days in month = 1, 2, ..., 12 */ +static const int mdays[14] = {0, 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31}; + +#define IN_RANGE(a,x,b) (((a) <= (x)) && ((x) <= (b))) +#define is_leap_year(y) (((((y) % 4) == 0) && \ + (((y) % 100) != 0)) || \ + (((y) % 400) == 0)) + +#define BASEYEAR 1970 + +/* + * gregday + * + * Returns the number of days since Jan 1, 1600, if year is + * greater of equal to 1600 , and month [1-12] and day [1-31] + * are within range. Otherwise it returns -1. + */ +static int long gregday(int year, int month, int day) +{ + int long ndays = 0; + int gyear, pyear, m; + + /* number of days in previous years */ + gyear = year - 1600; + if (gyear > 0) { + pyear = gyear - 1; + ndays = (pyear/4) - (pyear/100) + (pyear/400) + pyear*365 + 366; + } + /* number of days in all months preceeding month */ + for (m = 1; m < month; m++) + ndays += mdays[m]; + /* Extra day if leap year and March or later */ + if (is_leap_year(year) && (month > 2)) + ndays++; + ndays += day - 1; + return ndays - 135140; /* 135140 = Jan 1, 1970 */ +} + + + +int +local_to_univ(Sint *year, Sint *month, Sint *day, + Sint *hour, Sint *minute, Sint *second, int isdst) +{ + time_t the_clock; + struct tm *tm, t; +#ifdef HAVE_GMTIME_R + struct tm tmbuf; +#endif + + if (!(IN_RANGE(BASEYEAR, *year, INT_MAX - 1) && + IN_RANGE(1, *month, 12) && + IN_RANGE(1, *day, (mdays[*month] + + (*month == 2 + && (*year % 4 == 0) + && (*year % 100 != 0 || *year % 400 == 0)))) && + IN_RANGE(0, *hour, 23) && + IN_RANGE(0, *minute, 59) && + IN_RANGE(0, *second, 59))) { + return 0; + } + + t.tm_year = *year - 1900; + t.tm_mon = *month - 1; + t.tm_mday = *day; + t.tm_hour = *hour; + t.tm_min = *minute; + t.tm_sec = *second; + t.tm_isdst = isdst; + the_clock = mktime(&t); +#ifdef HAVE_GMTIME_R + gmtime_r(&the_clock, (tm = &tmbuf)); +#else + tm = gmtime(&the_clock); +#endif + *year = tm->tm_year + 1900; + *month = tm->tm_mon +1; + *day = tm->tm_mday; + *hour = tm->tm_hour; + *minute = tm->tm_min; + *second = tm->tm_sec; + return 1; +} + +int +univ_to_local(Sint *year, Sint *month, Sint *day, + Sint *hour, Sint *minute, Sint *second) +{ + time_t the_clock; + struct tm *tm; +#ifdef HAVE_LOCALTIME_R + struct tm tmbuf; +#endif + + if (!(IN_RANGE(BASEYEAR, *year, INT_MAX - 1) && + IN_RANGE(1, *month, 12) && + IN_RANGE(1, *day, (mdays[*month] + + (*month == 2 + && (*year % 4 == 0) + && (*year % 100 != 0 || *year % 400 == 0)))) && + IN_RANGE(0, *hour, 23) && + IN_RANGE(0, *minute, 59) && + IN_RANGE(0, *second, 59))) { + return 0; + } + + the_clock = *second + 60 * (*minute + 60 * (*hour + 24 * + gregday(*year, *month, *day))); +#ifdef HAVE_POSIX2TIME + /* + * Addition from OpenSource - affects FreeBSD. + * No valid test case /PaN + * + * leap-second correction performed + * if system is configured so; + * do nothing if not + * See FreeBSD 6.x and 7.x + * /usr/src/lib/libc/stdtime/localtime.c + * for the details + */ + the_clock = posix2time(the_clock); +#endif + +#ifdef HAVE_LOCALTIME_R + localtime_r(&the_clock, (tm = &tmbuf)); +#else + tm = localtime(&the_clock); +#endif + *year = tm->tm_year + 1900; + *month = tm->tm_mon +1; + *day = tm->tm_mday; + *hour = tm->tm_hour; + *minute = tm->tm_min; + *second = tm->tm_sec; + return 1; +} + + +/* get a timestamp */ +void +get_now(Uint* megasec, Uint* sec, Uint* microsec) +{ + SysTimeval now; + + erts_smp_mtx_lock(&erts_timeofday_mtx); + + get_tolerant_timeofday(&now); + do_erts_deliver_time(&now); + + /* Make sure time is later than last */ + if (then.tv_sec > now.tv_sec || + (then.tv_sec == now.tv_sec && then.tv_usec >= now.tv_usec)) { + now = then; + now.tv_usec++; + } + /* Check for carry from above + general reasonability */ + if (now.tv_usec >= 1000000) { + now.tv_usec = 0; + now.tv_sec++; + } + then = now; + + erts_smp_mtx_unlock(&erts_timeofday_mtx); + + *megasec = (Uint) (now.tv_sec / 1000000); + *sec = (Uint) (now.tv_sec % 1000000); + *microsec = (Uint) (now.tv_usec); +} + +void +get_sys_now(Uint* megasec, Uint* sec, Uint* microsec) +{ + SysTimeval now; + + erts_smp_mtx_lock(&erts_timeofday_mtx); + + sys_gettimeofday(&now); + + erts_smp_mtx_unlock(&erts_timeofday_mtx); + + *megasec = (Uint) (now.tv_sec / 1000000); + *sec = (Uint) (now.tv_sec % 1000000); + *microsec = (Uint) (now.tv_usec); +} + + +/* deliver elapsed *ticks* to the machine - takes a pointer + to a struct timeval representing current time (to save + a gettimeofday() where possible) or NULL */ + +#if !defined(ERTS_TIMER_THREAD) +void erts_deliver_time(void) { + SysTimeval now; + + erts_smp_mtx_lock(&erts_timeofday_mtx); + + get_tolerant_timeofday(&now); + do_erts_deliver_time(&now); + + erts_smp_mtx_unlock(&erts_timeofday_mtx); +} +#endif + +/* get *real* time (not ticks) remaining until next timeout - if there + isn't one, give a "long" time, that is guaranteed + to not cause overflow when we report elapsed time later on */ + +void erts_time_remaining(SysTimeval *rem_time) +{ + int ticks; +#if !defined(ERTS_TIMER_THREAD) + SysTimeval cur_time; +#endif + long elapsed; + + /* next_time() returns no of ticks to next timeout or -1 if none */ + + if ((ticks = next_time()) == -1) { + /* timer queue empty */ + /* this will cause at most 100000000 ticks */ + rem_time->tv_sec = 100000; + rem_time->tv_usec = 0; + } else { + /* next timeout after ticks ticks */ + ticks *= CLOCK_RESOLUTION; + +#if defined(ERTS_TIMER_THREAD) + elapsed = 0; +#else + erts_smp_mtx_lock(&erts_timeofday_mtx); + + get_tolerant_timeofday(&cur_time); + cur_time.tv_usec = 1000 * + (cur_time.tv_usec / 1000);/* ms resolution*/ + elapsed = 1000 * (cur_time.tv_sec - last_delivered.tv_sec) + + (cur_time.tv_usec - last_delivered.tv_usec) / 1000; + + erts_smp_mtx_unlock(&erts_timeofday_mtx); + + if (ticks <= elapsed) { /* Ooops, better hurry */ + rem_time->tv_sec = rem_time->tv_usec = 0; + return; + } +#endif + rem_time->tv_sec = (ticks - elapsed) / 1000; + rem_time->tv_usec = 1000 * ((ticks - elapsed) % 1000); + } +} + +void erts_get_timeval(SysTimeval *tv) +{ + erts_smp_mtx_lock(&erts_timeofday_mtx); + get_tolerant_timeofday(tv); + erts_smp_mtx_unlock(&erts_timeofday_mtx); +} + +long +erts_get_time(void) +{ + SysTimeval sys_tv; + + erts_smp_mtx_lock(&erts_timeofday_mtx); + + get_tolerant_timeofday(&sys_tv); + + erts_smp_mtx_unlock(&erts_timeofday_mtx); + + return sys_tv.tv_sec; +} + +#ifdef HAVE_ERTS_NOW_CPU +void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec) { + SysCpuTime t; + SysTimespec tp; + + sys_get_proc_cputime(t, tp); + *microsec = (Uint)(tp.tv_nsec / 1000); + t = (tp.tv_sec / 1000000); + *megasec = (Uint)(t % 1000000); + *sec = (Uint)(tp.tv_sec % 1000000); +} +#endif + + +/* + * erts_get_emu_time() is similar to get_now(). You will + * always get different times from erts_get_emu_time(), but they + * may equal a time from get_now(). + * + * erts_get_emu_time() is only used internally in the emulator in + * order to order emulator internal events. + */ + +void +erts_get_emu_time(SysTimeval *this_emu_time_p) +{ + erts_smp_mtx_lock(&erts_timeofday_mtx); + + get_tolerant_timeofday(this_emu_time_p); + + /* Make sure time is later than last */ + if (last_emu_time.tv_sec > this_emu_time_p->tv_sec || + (last_emu_time.tv_sec == this_emu_time_p->tv_sec + && last_emu_time.tv_usec >= this_emu_time_p->tv_usec)) { + *this_emu_time_p = last_emu_time; + this_emu_time_p->tv_usec++; + } + /* Check for carry from above + general reasonability */ + if (this_emu_time_p->tv_usec >= 1000000) { + this_emu_time_p->tv_usec = 0; + this_emu_time_p->tv_sec++; + } + + last_emu_time = *this_emu_time_p; + + erts_smp_mtx_unlock(&erts_timeofday_mtx); +} |