FS-3782 --resolve soft timer

This commit is contained in:
Jeff Lenk 2012-03-14 10:05:46 -05:00
parent 8c794acbcc
commit 4276680181
3 changed files with 151 additions and 50 deletions

View File

@ -260,6 +260,7 @@ struct switch_runtime {
uint32_t max_db_handles; uint32_t max_db_handles;
uint32_t db_handle_timeout; uint32_t db_handle_timeout;
int cpu_count; int cpu_count;
uint32_t time_sync;
}; };
extern struct switch_runtime runtime; extern struct switch_runtime runtime;

View File

@ -546,24 +546,19 @@ SWITCH_STANDARD_API(timer_test_function)
goto end; goto end;
} }
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Timer Test: samplecount after init: %d\n", timer.samplecount); switch_core_timer_next(&timer); /* Step timer once before testing results below, to get first timestamp as accurate as possible */
/* Step timer once before testing results below, to get first timestamp as accurate as possible */ start = then = switch_time_ref();
switch_core_timer_next(&timer);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Timer Test: samplecount after first step: %d\n", timer.samplecount);
start = switch_time_ref();
for (x = 1; x <= max; x++) { for (x = 1; x <= max; x++) {
then = switch_time_ref();
switch_core_timer_next(&timer); switch_core_timer_next(&timer);
now = switch_time_ref(); now = switch_time_ref();
diff = (int) (now - then); diff = (int) (now - then);
//stream->write_function(stream, "test %d sleep %ld %d\n", x, mss, diff);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Timer Test: %d sleep %d %d\n", x, mss, diff);
total += diff; total += diff;
then = now;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Timer Test: %d sleep %d %d\n", x, mss, diff);
} }
end = switch_time_ref(); end = then;
switch_yield(250000); switch_yield(250000);

View File

@ -59,9 +59,10 @@
#define MAX_ELEMENTS 3600 #define MAX_ELEMENTS 3600
#define IDLE_SPEED 100 #define IDLE_SPEED 100
/* In Windows, enable the montonic timer for better timer accuracy on Windows 2003 Server, XP and older */ /* In Windows, enable the montonic timer for better timer accuracy,
/* GetSystemTimeAsFileTime does not update on timeBeginPeriod on these OS. */ * GetSystemTimeAsFileTime does not update on timeBeginPeriod on these OS.
/* Flag SCF_USE_WIN32_MONOTONIC must be enabled to activate it (start parameter -monotonic-clock) */ * Flag SCF_USE_WIN32_MONOTONIC must be enabled to activate it (start parameter -monotonic-clock).
*/
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
static int MONO = 1; static int MONO = 1;
@ -91,9 +92,12 @@ static int COND = 1;
static int MATRIX = 1; static int MATRIX = 1;
#ifdef WIN32 #ifdef WIN32
static CRITICAL_SECTION timer_section;
static switch_time_t win32_tick_time_since_start = -1; static switch_time_t win32_tick_time_since_start = -1;
static DWORD win32_last_get_time_tick = 0; static DWORD win32_last_get_time_tick = 0;
CRITICAL_SECTION timer_section;
static uint8_t win32_use_qpc = 0;
static uint64_t win32_qpc_freq = 0;
#endif #endif
static switch_memory_pool_t *module_pool = NULL; static switch_memory_pool_t *module_pool = NULL;
@ -321,7 +325,7 @@ SWITCH_DECLARE(time_t) switch_epoch_time_now(time_t *t)
SWITCH_DECLARE(void) switch_time_set_monotonic(switch_bool_t enable) SWITCH_DECLARE(void) switch_time_set_monotonic(switch_bool_t enable)
{ {
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) #if (defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)) || defined(WIN32)
MONO = enable ? 1 : 0; MONO = enable ? 1 : 0;
switch_time_sync(); switch_time_sync();
#else #else
@ -375,24 +379,32 @@ static switch_time_t time_now(int64_t offset)
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec * APR_USEC_PER_SEC + (ts.tv_nsec / 1000) + offset; now = ts.tv_sec * APR_USEC_PER_SEC + (ts.tv_nsec / 1000) + offset;
#else #else
DWORD tick_now; if (win32_use_qpc) {
DWORD tick_diff; /* Use QueryPerformanceCounter */
uint64_t count = 0;
tick_now = timeGetTime(); QueryPerformanceCounter((LARGE_INTEGER*)&count);
if (win32_tick_time_since_start != -1) { now = ((count * 1000000) / win32_qpc_freq) + offset;
EnterCriticalSection(&timer_section);
/* just add diff (to make it work more than 50 days). */
tick_diff = tick_now - win32_last_get_time_tick;
win32_tick_time_since_start += tick_diff;
win32_last_get_time_tick = tick_now;
now = (win32_tick_time_since_start * 1000) + offset;
LeaveCriticalSection(&timer_section);
} else { } else {
/* If someone is calling us before timer is initialized, /* Use good old timeGetTime() */
* return the current tick + offset DWORD tick_now;
*/ DWORD tick_diff;
now = (tick_now * 1000) + offset;
tick_now = timeGetTime();
if (win32_tick_time_since_start != -1) {
EnterCriticalSection(&timer_section);
/* just add diff (to make it work more than 50 days). */
tick_diff = tick_now - win32_last_get_time_tick;
win32_tick_time_since_start += tick_diff;
win32_last_get_time_tick = tick_now;
now = (win32_tick_time_since_start * 1000) + offset;
LeaveCriticalSection(&timer_section);
} else {
/* If someone is calling us before timer is initialized,
* return the current tick + offset
*/
now = (tick_now * 1000) + offset;
}
} }
#endif #endif
} else { } else {
@ -415,6 +427,8 @@ static switch_time_t last_time = 0;
SWITCH_DECLARE(void) switch_time_sync(void) SWITCH_DECLARE(void) switch_time_sync(void)
{ {
runtime.time_sync++; /* Indicate that we are syncing time right now */
runtime.reference = switch_time_now(); runtime.reference = switch_time_now();
runtime.offset = runtime.reference - time_now(0); runtime.offset = runtime.reference - time_now(0);
@ -423,6 +437,8 @@ SWITCH_DECLARE(void) switch_time_sync(void)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Clock synchronized to system time.\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Clock synchronized to system time.\n");
} }
last_time = runtime.reference; last_time = runtime.reference;
runtime.time_sync++; /* Indicate that we are finished syncing time */
} }
SWITCH_DECLARE(void) switch_micro_sleep(switch_interval_time_t t) SWITCH_DECLARE(void) switch_micro_sleep(switch_interval_time_t t)
@ -546,6 +562,7 @@ static switch_status_t timer_init(switch_timer_t *timer)
#define check_roll() if (private_info->roll < TIMER_MATRIX[timer->interval].roll) { \ #define check_roll() if (private_info->roll < TIMER_MATRIX[timer->interval].roll) { \
private_info->roll++; \ private_info->roll++; \
private_info->reference = private_info->start = TIMER_MATRIX[timer->interval].tick; \ private_info->reference = private_info->start = TIMER_MATRIX[timer->interval].tick; \
private_info->start--; /* Must have a diff */ \
} \ } \
@ -562,7 +579,7 @@ static switch_status_t timer_step(switch_timer_t *timer)
samples = timer->samples * (private_info->reference - private_info->start); samples = timer->samples * (private_info->reference - private_info->start);
if (samples > UINT32_MAX) { if (samples > UINT32_MAX) {
private_info->start = private_info->reference; private_info->start = private_info->reference - 1; /* Must have a diff */
samples = timer->samples; samples = timer->samples;
} }
@ -691,6 +708,55 @@ static switch_status_t timer_destroy(switch_timer_t *timer)
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
static void win32_init_timers(void)
{
#ifdef WIN32
OSVERSIONINFOEX version_info; /* Used to fetch current OS version from Windows */
EnterCriticalSection(&timer_section);
ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
/* Check if we should use timeGetTime() (pre-Vista) or QueryPerformanceCounter() (Vista and later) */
if (GetVersionEx((OSVERSIONINFO*) &version_info)) {
if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT && version_info.dwMajorVersion >= 6) {
if (QueryPerformanceFrequency((LARGE_INTEGER*)&win32_qpc_freq) && win32_qpc_freq > 0) {
/* At least Vista, and QueryPerformanceFrequency() suceeded, enable qpc */
win32_use_qpc = 1;
} else {
/* At least Vista, but QueryPerformanceFrequency() failed, disable qpc */
win32_use_qpc = 0;
}
} else {
/* Older then Vista, disable qpc */
win32_use_qpc = 0;
}
} else {
/* Unknown version - we want at least Vista, disable qpc */
win32_use_qpc = 0;
}
if (win32_use_qpc) {
uint64_t count = 0;
if (!QueryPerformanceCounter((LARGE_INTEGER*)&count) || count == 0) {
/* Call to QueryPerformanceCounter() failed, disable qpc again */
win32_use_qpc = 0;
}
}
if (!win32_use_qpc) {
/* This will enable timeGetTime() instead, qpc init failed */
win32_last_get_time_tick = timeGetTime();
win32_tick_time_since_start = win32_last_get_time_tick;
}
LeaveCriticalSection(&timer_section);
#endif
}
SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime) SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
{ {
switch_time_t too_late = runtime.microseconds_per_tick * 1000; switch_time_t too_late = runtime.microseconds_per_tick * 1000;
@ -700,6 +766,7 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
int fwd_errs = 0, rev_errs = 0; int fwd_errs = 0, rev_errs = 0;
int profile_tick = 0; int profile_tick = 0;
int tfd = -1; int tfd = -1;
uint32_t time_sync = runtime.time_sync;
#ifdef HAVE_TIMERFD_CREATE #ifdef HAVE_TIMERFD_CREATE
int last_MICROSECONDS_PER_TICK = runtime.microseconds_per_tick; int last_MICROSECONDS_PER_TICK = runtime.microseconds_per_tick;
@ -738,6 +805,7 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
#endif #endif
switch_time_sync(); switch_time_sync();
time_sync = runtime.time_sync;
globals.STARTED = globals.RUNNING = 1; globals.STARTED = globals.RUNNING = 1;
switch_mutex_lock(runtime.throttle_mutex); switch_mutex_lock(runtime.throttle_mutex);
@ -775,6 +843,7 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
switch_time_sync(); switch_time_sync();
time_sync = runtime.time_sync;
globals.use_cond_yield = COND; globals.use_cond_yield = COND;
globals.RUNNING = 1; globals.RUNNING = 1;
@ -795,8 +864,12 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
while (((ts = time_now(runtime.offset)) + 100) < runtime.reference) { while (((ts = time_now(runtime.offset)) + 100) < runtime.reference) {
if (ts < last) { if (ts < last) {
if (MONO) { if (MONO) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Virtual Migration Detected! Syncing Clock\n"); if (time_sync == runtime.time_sync) { /* Only resync if not in the middle of switch_time_sync() already */
switch_time_sync(); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Virtual Migration Detected! Syncing Clock\n");
win32_init_timers(); /* Make sure to reinit timers on WIN32 */
switch_time_sync();
time_sync = runtime.time_sync;
}
} else { } else {
int64_t diff = (int64_t) (ts - last); int64_t diff = (int64_t) (ts - last);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Reverse Clock Skew Detected!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Reverse Clock Skew Detected!\n");
@ -807,9 +880,10 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
rev_errs++; rev_errs++;
} }
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, if (!MONO || time_sync == runtime.time_sync) {
"If you see this message many times try setting the param enable-clock-nanosleep to true in switch.conf.xml or consider a nicer machine to run me on. I AM *FREE* afterall.\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT,
"If you see this message many times try setting the param enable-clock-nanosleep to true in switch.conf.xml or consider a nicer machine to run me on. I AM *FREE* afterall.\n");
}
} else { } else {
rev_errs = 0; rev_errs = 0;
} }
@ -823,7 +897,19 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
r = read(tfd, &exp, sizeof(exp)); r = read(tfd, &exp, sizeof(exp));
r++; r++;
} else { } else {
do_sleep(runtime.microseconds_per_tick); switch_time_t timediff = runtime.reference - ts;
if (runtime.microseconds_per_tick < timediff) {
/* Only sleep for runtime.microseconds_per_tick if this value is lower then the actual time diff we need to sleep */
do_sleep(runtime.microseconds_per_tick);
} else {
#ifdef WIN32
/* Windows only sleeps in ms precision, try to round the usec value as good as possible */
do_sleep((switch_interval_time_t)floor((timediff / 1000.0) + 0.5) * 1000);
#else
do_sleep(timediff);
#endif
}
} }
} }
@ -832,13 +918,15 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
if (ts > (runtime.reference + too_late)) { if (ts > (runtime.reference + too_late)) {
if (MONO) { if (MONO) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Virtual Migration Detected! Syncing Clock\n"); if (time_sync == runtime.time_sync) { /* Only resync if not in the middle of switch_time_sync() already */
switch_time_sync(); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Virtual Migration Detected! Syncing Clock\n");
win32_init_timers(); /* Make sure to reinit timers on WIN32 */
switch_time_sync();
time_sync = runtime.time_sync;
}
} else { } else {
switch_time_t diff = ts - runtime.reference - runtime.microseconds_per_tick; switch_time_t diff = ts - runtime.reference - runtime.microseconds_per_tick;
#ifndef WIN32
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Forward Clock Skew Detected!\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Forward Clock Skew Detected!\n");
#endif
fwd_errs++; fwd_errs++;
runtime.reference = switch_time_now(); runtime.reference = switch_time_now();
current_ms = 0; current_ms = 0;
@ -852,6 +940,7 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
if (fwd_errs > 9 || rev_errs > 9) { if (fwd_errs > 9 || rev_errs > 9) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Auto Re-Syncing clock.\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Auto Re-Syncing clock.\n");
switch_time_sync(); switch_time_sync();
time_sync = runtime.time_sync;
fwd_errs = rev_errs = 0; fwd_errs = rev_errs = 0;
} }
@ -859,6 +948,10 @@ SWITCH_MODULE_RUNTIME_FUNCTION(softtimer_runtime)
current_ms += (runtime.microseconds_per_tick / 1000); current_ms += (runtime.microseconds_per_tick / 1000);
tick += (runtime.microseconds_per_tick / 1000); tick += (runtime.microseconds_per_tick / 1000);
if (time_sync < runtime.time_sync) {
time_sync++; /* Only step once for each loop, we want to make sure to keep this thread safe */
}
if (tick >= (1000000 / runtime.microseconds_per_tick)) { if (tick >= (1000000 / runtime.microseconds_per_tick)) {
if (++profile_tick == 1) { if (++profile_tick == 1) {
switch_get_system_idle_time(runtime.profile_timer, &runtime.profile_time); switch_get_system_idle_time(runtime.profile_timer, &runtime.profile_time);
@ -1117,11 +1210,12 @@ SWITCH_MODULE_LOAD_FUNCTION(softtimer_load)
switch_timer_interface_t *timer_interface; switch_timer_interface_t *timer_interface;
module_pool = pool; module_pool = pool;
#if defined(WIN32) #ifdef WIN32
timeBeginPeriod(1); timeBeginPeriod(1);
InitializeCriticalSection(&timer_section); InitializeCriticalSection(&timer_section);
win32_last_get_time_tick = timeGetTime();
win32_tick_time_since_start = win32_last_get_time_tick; win32_init_timers(); /* Init timers for Windows, if we should use timeGetTime() or QueryPerformanceCounters() */
#endif #endif
memset(&globals, 0, sizeof(globals)); memset(&globals, 0, sizeof(globals));
@ -1155,6 +1249,21 @@ SWITCH_MODULE_LOAD_FUNCTION(softtimer_load)
switch_clear_flag((&runtime), SCF_CALIBRATE_CLOCK); switch_clear_flag((&runtime), SCF_CALIBRATE_CLOCK);
} }
#ifdef WIN32
if (switch_test_flag((&runtime), SCF_USE_WIN32_MONOTONIC)) {
MONO = 1;
if (win32_use_qpc) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Enabled Windows monotonic clock, using QueryPerformanceCounter()\n");
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Enabled Windows monotonic clock, using timeGetTime()\n");
}
}
/* No need to calibrate clock in Win32, we will only sleep ms anyway, it's just not accurate enough */
switch_clear_flag((&runtime), SCF_CALIBRATE_CLOCK);
#endif
if (switch_test_flag((&runtime), SCF_CALIBRATE_CLOCK)) { if (switch_test_flag((&runtime), SCF_CALIBRATE_CLOCK)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Calibrating timer, please wait...\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Calibrating timer, please wait...\n");
switch_time_calibrate_clock(); switch_time_calibrate_clock();
@ -1162,10 +1271,6 @@ SWITCH_MODULE_LOAD_FUNCTION(softtimer_load)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Clock calibration disabled.\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Clock calibration disabled.\n");
} }
if (switch_test_flag((&runtime), SCF_USE_WIN32_MONOTONIC)) {
MONO = 1;
}
/* indicate that the module should continue to be loaded */ /* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }