Panda3D
|
00001 // Filename: trueClock.cxx 00002 // Created by: drose (04Jul00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 00016 #include "trueClock.h" 00017 #include "config_express.h" 00018 #include "numeric_types.h" 00019 00020 #include <math.h> // for fabs() 00021 00022 TrueClock *TrueClock::_global_ptr = NULL; 00023 00024 #if defined(WIN32_VC) || defined(WIN64_VC) 00025 00026 //////////////////////////////////////////////////////////////////// 00027 // 00028 // The Win32 implementation. 00029 // 00030 //////////////////////////////////////////////////////////////////// 00031 00032 #include <sys/timeb.h> 00033 #define WIN32_LEAN_AND_MEAN 00034 #include <windows.h> 00035 00036 static const double _0001 = 1.0 / 1000.0; 00037 static const double _00000001 = 1.0 / 10000000.0; 00038 00039 // This is the interval of time, in seconds, over which to measure the 00040 // high-precision clock rate vs. the time-of-day rate, when 00041 // paranoid-clock is in effect. Reducing it makes the clock respond 00042 // more quickly to changes in rate, but setting it too small may 00043 // introduce erratic behavior, especially if the user has ntp 00044 // configured. 00045 static const double paranoid_clock_interval = 3.0; 00046 00047 // It will be considered a clock jump error if either the 00048 // high-precision clock or the time-of-day clock change by this number 00049 // of seconds without the other jumping by a similar amount. 00050 static const double paranoid_clock_jump_error = 2.0; 00051 00052 // If the we detect a clock jump error but the corrected clock skew is 00053 // currently more than this amount, we hack the clock scale to try to 00054 // compensate. 00055 static const double paranoid_clock_jump_error_max_delta = 1.0; 00056 00057 // If the measured time_scale appears to change by more than this 00058 // factor, it will be reported to the log. Changes to time_scale less 00059 // than this factor are assumed to be within the margin of error. 00060 static const double paranoid_clock_report_scale_factor = 0.1; 00061 00062 // If the high-precision clock, after applying time_scale correction, 00063 // is still more than this number of seconds above or below the 00064 // time-of-day clock, it will be sped up or slowed down slightly until 00065 // it is back in sync. 00066 static const double paranoid_clock_chase_threshold = 0.5; 00067 00068 // This is the minimum factor by which the high-precision clock will 00069 // be sped up or slowed down when it gets out of sync by 00070 // paranoid-clock-chase-threshold. 00071 static const double paranoid_clock_chase_factor = 0.1; 00072 00073 //////////////////////////////////////////////////////////////////// 00074 // Function: TrueClock::get_long_time, Win32 implementation 00075 // Access: Published 00076 // Description: 00077 //////////////////////////////////////////////////////////////////// 00078 double TrueClock:: 00079 get_long_time() { 00080 int tc = GetTickCount(); 00081 return (double)(tc - _init_tc) * _0001; 00082 } 00083 00084 //////////////////////////////////////////////////////////////////// 00085 // Function: TrueClock::get_short_raw_time, Win32 implementation 00086 // Access: Published 00087 // Description: 00088 //////////////////////////////////////////////////////////////////// 00089 double TrueClock:: 00090 get_short_raw_time() { 00091 double time; 00092 00093 if (_has_high_res) { 00094 // Use the high-resolution clock. This is of questionable value, 00095 // since (a) on some OS's and hardware, the low 24 bits can 00096 // occasionally roll over without setting the carry bit, causing 00097 // the time to jump backwards, and (b) reportedly it can set the 00098 // carry bit incorrectly sometimes, causing the time to jump 00099 // forwards, and (c) even when it doesn't do that, it's not very 00100 // accurate and seems to lose seconds of time per hour, and (d) 00101 // someone could be running a program such as Speed Gear which 00102 // munges this value anyway. 00103 PN_int64 count; 00104 QueryPerformanceCounter((LARGE_INTEGER *)&count); 00105 00106 time = (double)(count - _init_count) * _recip_frequency; 00107 00108 } else { 00109 // No high-resolution clock; return the best information we have. 00110 // This doesn't suffer from the rollover problems that 00111 // QueryPerformanceCounter does, but it's not very precise--only 00112 // precise to 50ms on Win98, and 10ms on XP-based systems--and 00113 // Speed Gear still munges it. 00114 int tc = GetTickCount(); 00115 time = (double)(tc - _init_tc) * _0001; 00116 } 00117 00118 return time; 00119 } 00120 00121 //////////////////////////////////////////////////////////////////// 00122 // Function: TrueClock::set_cpu_affinity, Win32 implementation 00123 // Access: Published 00124 // Description: 00125 //////////////////////////////////////////////////////////////////// 00126 typedef BOOL (WINAPI * PFNSETPROCESSAFFINITYMASK)(HANDLE, DWORD_PTR); 00127 typedef BOOL (WINAPI * PFNGETPROCESSAFFINITYMASK)(HANDLE, PDWORD_PTR, PDWORD_PTR); 00128 00129 bool TrueClock:: 00130 set_cpu_affinity(PN_uint32 mask) const { 00131 HMODULE hker = GetModuleHandle("kernel32"); 00132 if (hker != 0) { 00133 PFNGETPROCESSAFFINITYMASK gp = (PFNGETPROCESSAFFINITYMASK) 00134 GetProcAddress(hker, "GetProcessAffinityMask"); 00135 PFNSETPROCESSAFFINITYMASK sp = (PFNSETPROCESSAFFINITYMASK) 00136 GetProcAddress(hker, "SetProcessAffinityMask"); 00137 if (gp != 0 && sp != 0) { 00138 DWORD proc_mask; 00139 DWORD sys_mask; 00140 if (gp(GetCurrentProcess(), (PDWORD_PTR)&proc_mask, (PDWORD_PTR)&sys_mask)) { 00141 // make sure we don't reference CPUs that don't exist 00142 proc_mask = mask & sys_mask; 00143 if (proc_mask) { 00144 return sp(GetCurrentProcess(), proc_mask) != 0; 00145 } 00146 } 00147 } 00148 } 00149 return false; 00150 } 00151 00152 //////////////////////////////////////////////////////////////////// 00153 // Function: TrueClock::Constructor, Win32 implementation 00154 // Access: Protected 00155 // Description: 00156 //////////////////////////////////////////////////////////////////// 00157 TrueClock:: 00158 TrueClock() { 00159 _error_count = 0; 00160 _has_high_res = false; 00161 00162 _time_scale = 1.0; 00163 _time_offset = 0.0; 00164 _tod_offset = 0.0; 00165 _time_scale_changed = false; 00166 _last_reported_time_scale = 1.0; 00167 _report_time_scale_time = 0.0; 00168 00169 ConfigVariableBool lock_to_one_cpu 00170 ("lock-to-one-cpu", false, 00171 PRC_DESC("Set this to true if you want the entire process to use one " 00172 "CPU, even on multi-core and multi-CPU workstations. This is " 00173 "mainly a hack to solve a bug in which QueryPerformanceCounter " 00174 "returns inconsistent results on multi-core machines. ")); 00175 00176 if (lock_to_one_cpu) { 00177 set_cpu_affinity(0x01); 00178 } 00179 00180 if (get_use_high_res_clock()) { 00181 PN_int64 int_frequency; 00182 _has_high_res = 00183 (QueryPerformanceFrequency((LARGE_INTEGER *)&int_frequency) != 0); 00184 if (_has_high_res) { 00185 if (int_frequency <= 0) { 00186 clock_cat.error() 00187 << "TrueClock::get_real_time() - frequency is negative!" << endl; 00188 _has_high_res = false; 00189 00190 } else { 00191 _frequency = (double)int_frequency; 00192 _recip_frequency = 1.0 / _frequency; 00193 00194 QueryPerformanceCounter((LARGE_INTEGER *)&_init_count); 00195 } 00196 } 00197 } 00198 00199 // Also store the initial tick count. We'll need this for 00200 // get_long_time(), as well as for get_short_time() if we're not 00201 // using the high resolution clock. 00202 _init_tc = GetTickCount(); 00203 00204 // And we will need the current time of day to cross-check either of 00205 // the above clocks if paranoid-clock is enabled. 00206 GetSystemTimeAsFileTime((FILETIME *)&_init_tod); 00207 00208 _chase_clock = CC_keep_even; 00209 00210 // In case we'll be cross-checking the clock, we'd better start out 00211 // with at least one timestamp, so we'll know if the clock jumps 00212 // just after startup. 00213 _timestamps.push_back(Timestamp(0.0, 0.0)); 00214 00215 if (!_has_high_res) { 00216 clock_cat.warning() 00217 << "No high resolution clock available." << endl; 00218 } 00219 } 00220 00221 //////////////////////////////////////////////////////////////////// 00222 // Function: TrueClock::correct_time, Win32 implementation 00223 // Access: Protected 00224 // Description: Ensures that the reported timestamp from the 00225 // high-precision (or even the low-precision) clock is 00226 // valid by verifying against the time-of-day clock. 00227 // 00228 // This attempts to detect sudden jumps in time that 00229 // might be caused by a failure of the high-precision 00230 // clock to roll over properly. 00231 // 00232 // It also corrects for long-term skew of the clock by 00233 // measuring the timing discrepency against the wall 00234 // clock and projecting that discrepency into the 00235 // future. This also should defeat programs such as 00236 // Speed Gear that work by munging the value returned by 00237 // QueryPerformanceCounter() and GetTickCount(), but not 00238 // the wall clock time. 00239 // 00240 // However, relying on wall clock time presents its own 00241 // set of problems, since the time of day might be 00242 // adjusted slightly forward or back from time to time 00243 // in response to ntp messages, or it might even be 00244 // suddenly reset at any time by the user. So we do the 00245 // best we can. 00246 //////////////////////////////////////////////////////////////////// 00247 double TrueClock:: 00248 correct_time(double time) { 00249 // First, get the current time of day measurement. 00250 PN_uint64 int_tod; 00251 GetSystemTimeAsFileTime((FILETIME *)&int_tod); 00252 double tod = (double)(int_tod - _init_tod) * _00000001; 00253 00254 nassertr(!_timestamps.empty(), time); 00255 00256 // Make sure we didn't experience a sudden jump from the last 00257 // measurement. 00258 double time_delta = (time - _timestamps.back()._time) * _time_scale; 00259 double tod_delta = (tod - _timestamps.back()._tod); 00260 00261 if (time_delta < -0.0001 || 00262 fabs(time_delta - tod_delta) > paranoid_clock_jump_error) { 00263 // A step backward in the high-precision clock, or more than a 00264 // small jump on only one of the clocks, is cause for alarm. We 00265 // allow a trivial step backward in the high-precision clock, 00266 // since this does appear to happen in a threaded environment. 00267 00268 clock_cat.debug() 00269 << "Clock error detected; elapsed time " << time_delta 00270 << "s on high-resolution counter, and " << tod_delta 00271 << "s on time-of-day clock.\n"; 00272 ++_error_count; 00273 00274 // If both are negative, we call it 0. If one is negative, we 00275 // trust the other one (up to paranoid_clock_jump_error). If both 00276 // are nonnegative, we trust the smaller of the two. 00277 double time_adjust = 0.0; 00278 double tod_adjust = 0.0; 00279 00280 if (time_delta < 0.0 && tod < 0.0) { 00281 // Trust neither. 00282 time_adjust = -time_delta; 00283 tod_adjust = -tod_delta; 00284 00285 } else if (time_delta < 0.0 || (tod_delta >= 0.0 && tod_delta < time_delta)) { 00286 // Trust tod, up to a point. 00287 double new_tod_delta = min(tod_delta, paranoid_clock_jump_error); 00288 time_adjust = new_tod_delta - time_delta; 00289 tod_adjust = new_tod_delta - tod_delta; 00290 00291 } else { 00292 // Trust time, up to a point. 00293 double new_time_delta = min(time_delta, paranoid_clock_jump_error); 00294 time_adjust = new_time_delta - time_delta; 00295 tod_adjust = new_time_delta - tod_delta; 00296 } 00297 00298 _time_offset += time_adjust; 00299 time_delta += time_adjust; 00300 _tod_offset += tod_adjust; 00301 tod_delta += tod_adjust; 00302 00303 // Apply the adjustments to the timestamp queue. We could just 00304 // completely empty the timestamp queue, but that makes it hard to 00305 // catch up if we are getting lots of these "momentary" errors in 00306 // a row. 00307 Timestamps::iterator ti; 00308 for (ti = _timestamps.begin(); ti != _timestamps.end(); ++ti) { 00309 (*ti)._time -= time_adjust / _time_scale; 00310 (*ti)._tod -= tod_adjust; 00311 } 00312 00313 // And now we can record this timestamp, which is now consistent 00314 // with the previous timestamps in the queue. 00315 _timestamps.push_back(Timestamp(time, tod)); 00316 00317 // Detecting and filtering this kind of momentary error can help 00318 // protect us from legitimate problems cause by OS or BIOS bugs 00319 // (which might introduce errors into the high precision clock), 00320 // or from sudden changes to the time-of-day by the user, but we 00321 // have to be careful because if the user uses a Speed Gear-type 00322 // program to speed up the clock by an extreme amount, it can look 00323 // like a lot of such "momentary" errors in a row--and if we throw 00324 // them all out, we won't compute _time_scale correctly. To avoid 00325 // this, we hack _time_scale here if we seem to be getting out of 00326 // sync. 00327 double corrected_time = time * _time_scale + _time_offset; 00328 double corrected_tod = tod + _tod_offset; 00329 if (corrected_time - corrected_tod > paranoid_clock_jump_error_max_delta && 00330 _time_scale > 0.00001) { 00331 clock_cat.info() 00332 << "Force-adjusting time_scale to catch up to errors.\n"; 00333 set_time_scale(time, _time_scale * 0.5); 00334 } 00335 00336 } else if (tod_delta < 0.0) { 00337 // A small backwards jump on the time-of-day clock is not a 00338 // concern, since this is technically allowed with ntp enabled. 00339 // We simply ignore the event. 00340 00341 } else { 00342 // Ok, we don't think there was a sudden jump, so carry on. 00343 00344 // The timestamp queue here records the measured timestamps over 00345 // the past _priority_interval seconds. Its main purpose is to 00346 // keep a running observation of _time_scale, so we can detect 00347 // runtime changes of the clock's scale, for instance if the user 00348 // is using a program like Speed Gear and pulls the slider during 00349 // runtime. 00350 00351 // Consider the oldest timestamp in our queue. 00352 Timestamp oldest = _timestamps.front(); 00353 double time_age = (time - oldest._time); 00354 double tod_age = (tod - oldest._tod); 00355 00356 double keep_interval = paranoid_clock_interval; 00357 00358 if (tod_age > keep_interval / 2.0 && time_age > 0.0) { 00359 // Adjust the _time_scale value to match the ratio between the 00360 // elapsed time on the high-resolution clock, and the 00361 // time-of-day clock. 00362 double new_time_scale = tod_age / time_age; 00363 00364 // When we adjust _time_scale, we have to be careful to adjust 00365 // _time_offset at the same time, so we don't introduce a 00366 // sudden jump in time. 00367 set_time_scale(time, new_time_scale); 00368 00369 // Check to see if the time scale has changed significantly 00370 // since we last reported it. 00371 double ratio = _time_scale / _last_reported_time_scale; 00372 if (fabs(ratio - 1.0) > paranoid_clock_report_scale_factor) { 00373 _time_scale_changed = true; 00374 _last_reported_time_scale = _time_scale; 00375 // Actually report it a little bit later, to give the time 00376 // scale a chance to settle down. 00377 _report_time_scale_time = tod + _tod_offset + keep_interval; 00378 if (clock_cat.is_debug()) { 00379 clock_cat.debug() 00380 << "Will report time scale, now " << 100.0 / _time_scale 00381 << "%, tod_age = " << tod_age << ", time_age = " << time_age 00382 << ", ratio = " << ratio << "\n"; 00383 } 00384 } 00385 } 00386 00387 // Clean out old entries in the timestamps queue. 00388 if (tod_age > keep_interval) { 00389 while (!_timestamps.empty() && 00390 tod - _timestamps.front()._tod > keep_interval) { 00391 _timestamps.pop_front(); 00392 } 00393 } 00394 00395 // Record this timestamp. 00396 _timestamps.push_back(Timestamp(time, tod)); 00397 } 00398 00399 double corrected_time = time * _time_scale + _time_offset; 00400 double corrected_tod = tod + _tod_offset; 00401 00402 if (_time_scale_changed && corrected_tod >= _report_time_scale_time) { 00403 double percent = 100.0 / _time_scale; 00404 // Round percent to the nearest 5% to reduce confusion in the logs. 00405 percent = floor(percent / 20.0 + 0.5) * 20.0; 00406 clock_cat.info() 00407 << "Clock appears to be running at " << percent << "% real time.\n"; 00408 _last_reported_time_scale = _time_scale; 00409 _time_scale_changed = false; 00410 } 00411 00412 // By the time we get here, we have a corrected_time and a 00413 // corrected_tod value, both of which should be advancing at about 00414 // the same rate. However, there might be accumulated skew between 00415 // them, since there is some lag in the above algorithm that 00416 // corrects the _time_scale, and clock skew can accumulate while the 00417 // algorithm is catching up. 00418 00419 // Therefore, we have one more line of defense: we check at this 00420 // point for skew, and correct for it by slowing the clock down or 00421 // speeding it up a bit as needed, until we even out the clocks 00422 // again. Rather than adjusting the clock speed with _time_scale 00423 // here, we simply slide _time_offset forward and back as 00424 // needed--that way we don't interfere with the above algorithm, 00425 // which is trying to compute _time_scale accurately. 00426 00427 switch (_chase_clock) { 00428 case CC_slow_down: 00429 if (corrected_time < corrected_tod) { 00430 // We caught up. 00431 _chase_clock = CC_keep_even; 00432 if (clock_cat.is_debug()) { 00433 clock_cat.debug() 00434 << "Clock back down to real time.\n"; 00435 // Let's report the clock error now, so an app can resync now 00436 // that we're at a good time. 00437 ++_error_count; 00438 } 00439 00440 } else { 00441 // Slow down the clock by sliding the offset a bit backward. 00442 double fixup = 1.0 - (1.0 / (corrected_time - corrected_tod)); 00443 double correction = time_delta * max(fixup, paranoid_clock_chase_factor); 00444 _time_offset -= correction; 00445 corrected_time -= correction; 00446 } 00447 break; 00448 00449 case CC_keep_even: 00450 if ((corrected_tod - corrected_time) > paranoid_clock_chase_threshold) { 00451 // Oops, we're dropping behind; need to speed up. 00452 _chase_clock = CC_speed_up; 00453 00454 if (clock_cat.is_debug()) { 00455 clock_cat.debug() 00456 << "Clock is behind by " << (corrected_tod - corrected_time) 00457 << "s; speeding up to correct.\n"; 00458 } 00459 } else if ((corrected_time - corrected_tod) > paranoid_clock_chase_threshold) { 00460 // Oops, we're going too fast; need to slow down. 00461 _chase_clock = CC_slow_down; 00462 00463 if (clock_cat.is_debug()) { 00464 clock_cat.debug() 00465 << "Clock is ahead by " << (corrected_time - corrected_tod) 00466 << "s; slowing down to correct.\n"; 00467 } 00468 } 00469 break; 00470 00471 case CC_speed_up: 00472 if (corrected_time > corrected_tod) { 00473 // We caught up. 00474 _chase_clock = CC_keep_even; 00475 if (clock_cat.is_debug()) { 00476 clock_cat.debug() 00477 << "Clock back up to real time.\n"; 00478 // Let's report the clock error now, so an app can resync now 00479 // that we're at a good time. 00480 ++_error_count; 00481 } 00482 00483 } else { 00484 // Speed up the clock by sliding the offset a bit forward. 00485 double fixup = 1.0 - (1.0 / (corrected_tod - corrected_time)); 00486 double correction = time_delta * max(fixup, paranoid_clock_chase_factor); 00487 _time_offset += correction; 00488 corrected_time += correction; 00489 } 00490 break; 00491 } 00492 00493 if (clock_cat.is_spam()) { 00494 clock_cat.spam() 00495 << "time " << time << " tod " << corrected_tod 00496 << " corrected time " << corrected_time << "\n"; 00497 } 00498 00499 return corrected_time; 00500 } 00501 00502 //////////////////////////////////////////////////////////////////// 00503 // Function: TrueClock::set_time_scale, Win32 implementation 00504 // Access: Protected 00505 // Description: Changes the _time_scale value, recomputing 00506 // _time_offset at the same time so we don't introduce a 00507 // sudden jump in time. 00508 //////////////////////////////////////////////////////////////////// 00509 void TrueClock:: 00510 set_time_scale(double time, double new_time_scale) { 00511 nassertv(new_time_scale > 0.0); 00512 _time_offset = time * _time_scale + _time_offset - (time * new_time_scale); 00513 _time_scale = new_time_scale; 00514 } 00515 00516 #else // !WIN32_VC 00517 00518 //////////////////////////////////////////////////////////////////// 00519 // 00520 // The Posix implementation. 00521 // 00522 //////////////////////////////////////////////////////////////////// 00523 00524 #include <sys/time.h> 00525 #include <stdio.h> // for perror 00526 00527 static long _init_sec; 00528 00529 //////////////////////////////////////////////////////////////////// 00530 // Function: TrueClock::get_long_time, Posix implementation 00531 // Access: Published 00532 // Description: 00533 //////////////////////////////////////////////////////////////////// 00534 double TrueClock:: 00535 get_long_time() { 00536 struct timeval tv; 00537 00538 int result; 00539 00540 #ifdef GETTIMEOFDAY_ONE_PARAM 00541 result = gettimeofday(&tv); 00542 #else 00543 result = gettimeofday(&tv, (struct timezone *)NULL); 00544 #endif 00545 00546 if (result < 0) { 00547 // Error in gettimeofday(). 00548 return 0.0; 00549 } 00550 00551 // We subtract out the time at which the clock was initialized, 00552 // because we don't care about the number of seconds all the way 00553 // back to 1970, and we want to leave the double with as much 00554 // precision as it can get. 00555 return (double)(tv.tv_sec - _init_sec) + (double)tv.tv_usec / 1000000.0; 00556 } 00557 00558 //////////////////////////////////////////////////////////////////// 00559 // Function: TrueClock::get_short_raw_time, Posix implementation 00560 // Access: Published 00561 // Description: 00562 //////////////////////////////////////////////////////////////////// 00563 double TrueClock:: 00564 get_short_raw_time() { 00565 struct timeval tv; 00566 00567 int result; 00568 00569 #ifdef GETTIMEOFDAY_ONE_PARAM 00570 result = gettimeofday(&tv); 00571 #else 00572 result = gettimeofday(&tv, (struct timezone *)NULL); 00573 #endif 00574 00575 if (result < 0) { 00576 // Error in gettimeofday(). 00577 return 0.0; 00578 } 00579 00580 // We subtract out the time at which the clock was initialized, 00581 // because we don't care about the number of seconds all the way 00582 // back to 1970, and we want to leave the double with as much 00583 // precision as it can get. 00584 return (double)(tv.tv_sec - _init_sec) + (double)tv.tv_usec / 1000000.0; 00585 } 00586 00587 //////////////////////////////////////////////////////////////////// 00588 // Function: TrueClock::set_cpu_affinity, Posix implementation 00589 // Access: Published 00590 // Description: 00591 //////////////////////////////////////////////////////////////////// 00592 bool TrueClock:: 00593 set_cpu_affinity(PN_uint32 mask) const { 00594 return false; 00595 } 00596 00597 //////////////////////////////////////////////////////////////////// 00598 // Function: TrueClock::Constructor, Posix implementation 00599 // Access: Protected 00600 // Description: 00601 //////////////////////////////////////////////////////////////////// 00602 TrueClock:: 00603 TrueClock() { 00604 _error_count = 0; 00605 struct timeval tv; 00606 00607 int result; 00608 #ifdef GETTIMEOFDAY_ONE_PARAM 00609 result = gettimeofday(&tv); 00610 #else 00611 result = gettimeofday(&tv, (struct timezone *)NULL); 00612 #endif 00613 00614 if (result < 0) { 00615 perror("gettimeofday"); 00616 _init_sec = 0; 00617 } else { 00618 _init_sec = tv.tv_sec; 00619 } 00620 } 00621 00622 #endif