/* * %CopyrightBegin% * * Copyright Ericsson AB 1999-2010. 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); if (the_clock == -1) { if (isdst) { /* If this is a timezone without DST and the OS (correctly) refuses to give us a DST time, we simulate the Linux/Solaris behaviour of giving the same data as if is_dst was not set. */ t.tm_isdst = 0; the_clock = mktime(&t); if (the_clock == -1) { /* Failed anyway, something else is bad - will be a badarg */ return 0; } } else { /* Something else is the matter, badarg. */ return 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; return 1; } #if defined(HAVE_POSIX2TIME) && defined(HAVE_DECL_POSIX2TIME) && \ !HAVE_DECL_POSIX2TIME extern time_t posix2time(time_t); #endif 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; sys_gettimeofday(&now); *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); }