/* * %CopyrightBegin% * * Copyright Ericsson AB 1999-2012. 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() */ union { erts_smp_atomic_t time; char align[ERTS_CACHE_LINE_SIZE]; } approx erts_align_attribute(ERTS_CACHE_LINE_SIZE); static void init_approx_time(void) { erts_smp_atomic_init_nob(&approx.time, 0); } static ERTS_INLINE erts_approx_time_t get_approx_time(void) { return (erts_approx_time_t) erts_smp_atomic_read_nob(&approx.time); } static ERTS_INLINE void update_approx_time(SysTimeval *tv) { erts_approx_time_t new_secs = (erts_approx_time_t) tv->tv_sec; erts_approx_time_t old_secs = get_approx_time(); if (old_secs != new_secs) erts_smp_atomic_set_nob(&approx.time, new_secs); } /* * erts_get_approx_time() returns an *approximate* time * in seconds. NOTE that this time may jump backwards!!! */ erts_approx_time_t erts_get_approx_time(void) { return get_approx_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 should 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. */ 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; erts_time_t 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) { ASSERT(elapsed < ((erts_time_t) ERTS_SHORT_TIME_T_MAX)); erts_do_time_add((erts_short_time_t) elapsed); last_delivered = cur_time; } } int erts_init_time_sup(void) { erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday"); init_approx_time(); 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_deliver_time(); return CLOCK_RESOLUTION; } /* info functions */ void elapsed_time_both(UWord *ms_user, UWord *ms_sys, UWord *ms_user_diff, UWord *ms_sys_diff) { UWord prev_total_user, prev_total_sys; UWord 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(UWord *ms_total, UWord *ms_diff) { UWord 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 tm = localtime_r(&the_clock, &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 tm = localtime_r(&the_clock, &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)) /* This is the earliest year we are sure to be able to handle on all platforms w/o problems */ #define BASEYEAR 1902 /* A more "clever" mktime * return 1, if successful * return -1, if not successful */ static int erl_mktime(time_t *c, struct tm *tm) { time_t clock; clock = mktime(tm); if (clock != -1) { *c = clock; return 1; } /* in rare occasions mktime returns -1 * when a correct value has been entered * * decrease seconds with one second * if the result is -2, epochs should be -1 */ tm->tm_sec = tm->tm_sec - 1; clock = mktime(tm); tm->tm_sec = tm->tm_sec + 1; *c = -1; if (clock == -2) { return 1; } return -1; } /* * 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 time_t gregday(int year, int month, int day) { Sint ndays = 0; Sint 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 (time_t) (ndays - 135140); /* 135140 = Jan 1, 1970 */ } #define SECONDS_PER_MINUTE (60) #define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE) #define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR) int seconds_to_univ(Sint64 time, Sint *year, Sint *month, Sint *day, Sint *hour, Sint *minute, Sint *second) { Sint y,mi; Sint days = time / SECONDS_PER_DAY; Sint secs = time % SECONDS_PER_DAY; Sint tmp; if (secs < 0) { days--; secs += SECONDS_PER_DAY; } tmp = secs % SECONDS_PER_HOUR; *hour = secs / SECONDS_PER_HOUR; *minute = tmp / SECONDS_PER_MINUTE; *second = tmp % SECONDS_PER_MINUTE; days += 719468; y = (10000*((Sint64)days) + 14780) / 3652425; tmp = days - (365 * y + y/4 - y/100 + y/400); if (tmp < 0) { y--; tmp = days - (365*y + y/4 - y/100 + y/400); } mi = (100 * tmp + 52)/3060; *month = (mi + 2) % 12 + 1; *year = y + (mi + 2) / 12; *day = tmp - (mi * 306 + 5)/10 + 1; return 1; } int univ_to_seconds(Sint year, Sint month, Sint day, Sint hour, Sint minute, Sint second, Sint64 *time) { Sint days; if (!(IN_RANGE(1600, 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; } days = gregday(year, month, day); *time = SECONDS_PER_DAY; *time *= days; /* don't try overflow it, it hurts */ *time += SECONDS_PER_HOUR * hour; *time += SECONDS_PER_MINUTE * minute; *time += second; return 1; } #if defined(HAVE_TIME2POSIX) && defined(HAVE_DECL_TIME2POSIX) && \ !HAVE_DECL_TIME2POSIX extern time_t time2posix(time_t); #endif 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 nature of mktime makes this a bit interesting, * up to four mktime calls could happen here */ if (erl_mktime(&the_clock, &t) < 0) { 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; if (erl_mktime(&the_clock, &t) < 0) { /* Failed anyway, something else is bad - will be a badarg */ return 0; } } else { /* Something else is the matter, badarg. */ return 0; } } #ifdef HAVE_TIME2POSIX the_clock = time2posix(the_clock); #endif #ifdef HAVE_GMTIME_R tm = gmtime_r(&the_clock, &tmbuf); #else tm = gmtime(&the_clock); #endif if (!tm) { return 0; } *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 tm = localtime_r(&the_clock, &tmbuf); #else tm = localtime(&the_clock); #endif if (tm) { *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; } return 0; } /* 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); update_approx_time(&now); } 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); update_approx_time(&now); } /* deliver elapsed *ticks* to the machine - takes a pointer to a struct timeval representing current time (to save a gettimeofday() where possible) or NULL */ 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); update_approx_time(&now); } /* 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) { erts_time_t ticks; SysTimeval cur_time; erts_time_t elapsed; /* erts_next_time() returns no of ticks to next timeout or -1 if none */ ticks = (erts_time_t) erts_next_time(); if (ticks == (erts_time_t) -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; 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; } 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); update_approx_time(tv); } erts_time_t 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); update_approx_time(&sys_tv); 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