⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 erl_time_sup.c

📁 OTP是开放电信平台的简称
💻 C
📖 第 1 页 / 共 2 页
字号:
/* ``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 via the world wide web 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. *  * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' *  *     $Id$ *//*** 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 */#ifdef HAVE_GETHRTIMEint 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)#elsetypedef 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)) #elseSysTimes dummy_tms;#define KERNEL_TICKS() (sys_times(&dummy_tms) &  \			((1UL << ((sizeof(clock_t) * 8) - 1)) - 1)) #endifstatic 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(&current_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_RESOLUTIONstatic 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) { }#elsestatic 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;    }}#endifint erts_init_time_sup(void){    erts_smp_mtx_init(&erts_timeofday_mtx, "timeofday");#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;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -