16 #include "trueClock.h"
17 #include "config_express.h"
18 #include "numeric_types.h"
24 #if defined(WIN32_VC) || defined(WIN64_VC)
32 #include <sys/timeb.h>
33 #ifndef WIN32_LEAN_AND_MEAN
34 #define WIN32_LEAN_AND_MEAN 1
38 static const double _0001 = 1.0 / 1000.0;
39 static const double _00000001 = 1.0 / 10000000.0;
47 static const double paranoid_clock_interval = 3.0;
52 static const double paranoid_clock_jump_error = 2.0;
57 static const double paranoid_clock_jump_error_max_delta = 1.0;
62 static const double paranoid_clock_report_scale_factor = 0.1;
68 static const double paranoid_clock_chase_threshold = 0.5;
73 static const double paranoid_clock_chase_factor = 0.1;
82 int tc = GetTickCount();
83 return (
double)(tc - _init_tc) * _0001;
92 get_short_raw_time() {
106 QueryPerformanceCounter((LARGE_INTEGER *)&count);
108 time = (double)(count - _init_count) * _recip_frequency;
116 int tc = GetTickCount();
117 time = (double)(tc - _init_tc) * _0001;
128 typedef BOOL (WINAPI * PFNSETPROCESSAFFINITYMASK)(HANDLE, DWORD_PTR);
129 typedef BOOL (WINAPI * PFNGETPROCESSAFFINITYMASK)(HANDLE, PDWORD_PTR, PDWORD_PTR);
132 set_cpu_affinity(PN_uint32 mask)
const {
133 HMODULE hker = GetModuleHandle(
"kernel32");
135 PFNGETPROCESSAFFINITYMASK gp = (PFNGETPROCESSAFFINITYMASK)
136 GetProcAddress(hker,
"GetProcessAffinityMask");
137 PFNSETPROCESSAFFINITYMASK sp = (PFNSETPROCESSAFFINITYMASK)
138 GetProcAddress(hker,
"SetProcessAffinityMask");
139 if (gp != 0 && sp != 0) {
142 if (gp(GetCurrentProcess(), (PDWORD_PTR)&proc_mask, (PDWORD_PTR)&sys_mask)) {
144 proc_mask = mask & sys_mask;
146 return sp(GetCurrentProcess(), proc_mask) != 0;
162 _has_high_res =
false;
167 _time_scale_changed =
false;
168 _last_reported_time_scale = 1.0;
169 _report_time_scale_time = 0.0;
172 (
"lock-to-one-cpu",
false,
173 PRC_DESC(
"Set this to true if you want the entire process to use one "
174 "CPU, even on multi-core and multi-CPU workstations. This is "
175 "mainly a hack to solve a bug in which QueryPerformanceCounter "
176 "returns inconsistent results on multi-core machines. "));
178 if (lock_to_one_cpu) {
179 set_cpu_affinity(0x01);
182 if (get_use_high_res_clock()) {
183 PN_int64 int_frequency;
185 (QueryPerformanceFrequency((LARGE_INTEGER *)&int_frequency) != 0);
187 if (int_frequency <= 0) {
189 <<
"TrueClock::get_real_time() - frequency is negative!" << endl;
190 _has_high_res =
false;
193 _frequency = (double)int_frequency;
194 _recip_frequency = 1.0 / _frequency;
196 QueryPerformanceCounter((LARGE_INTEGER *)&_init_count);
204 _init_tc = GetTickCount();
208 GetSystemTimeAsFileTime((FILETIME *)&_init_tod);
210 _chase_clock = CC_keep_even;
215 _timestamps.push_back(Timestamp(0.0, 0.0));
217 if (!_has_high_res) {
219 <<
"No high resolution clock available." << endl;
250 correct_time(
double time) {
253 GetSystemTimeAsFileTime((FILETIME *)&int_tod);
254 double tod = (double)(int_tod - _init_tod) * _00000001;
256 nassertr(!_timestamps.empty(), time);
260 double time_delta = (time - _timestamps.back()._time) * _time_scale;
261 double tod_delta = (tod - _timestamps.back()._tod);
263 if (time_delta < -0.0001 ||
264 fabs(time_delta - tod_delta) > paranoid_clock_jump_error) {
271 <<
"Clock error detected; elapsed time " << time_delta
272 <<
"s on high-resolution counter, and " << tod_delta
273 <<
"s on time-of-day clock.\n";
279 double time_adjust = 0.0;
280 double tod_adjust = 0.0;
282 if (time_delta < 0.0 && tod < 0.0) {
284 time_adjust = -time_delta;
285 tod_adjust = -tod_delta;
287 }
else if (time_delta < 0.0 || (tod_delta >= 0.0 && tod_delta < time_delta)) {
289 double new_tod_delta = min(tod_delta, paranoid_clock_jump_error);
290 time_adjust = new_tod_delta - time_delta;
291 tod_adjust = new_tod_delta - tod_delta;
295 double new_time_delta = min(time_delta, paranoid_clock_jump_error);
296 time_adjust = new_time_delta - time_delta;
297 tod_adjust = new_time_delta - tod_delta;
300 _time_offset += time_adjust;
301 time_delta += time_adjust;
302 _tod_offset += tod_adjust;
303 tod_delta += tod_adjust;
309 Timestamps::iterator ti;
310 for (ti = _timestamps.begin(); ti != _timestamps.end(); ++ti) {
311 (*ti)._time -= time_adjust / _time_scale;
312 (*ti)._tod -= tod_adjust;
317 _timestamps.push_back(Timestamp(time, tod));
329 double corrected_time = time * _time_scale + _time_offset;
330 double corrected_tod = tod + _tod_offset;
331 if (corrected_time - corrected_tod > paranoid_clock_jump_error_max_delta &&
332 _time_scale > 0.00001) {
334 <<
"Force-adjusting time_scale to catch up to errors.\n";
335 set_time_scale(time, _time_scale * 0.5);
338 }
else if (tod_delta < 0.0) {
354 Timestamp oldest = _timestamps.front();
355 double time_age = (time - oldest._time);
356 double tod_age = (tod - oldest._tod);
358 double keep_interval = paranoid_clock_interval;
360 if (tod_age > keep_interval / 2.0 && time_age > 0.0) {
364 double new_time_scale = tod_age / time_age;
369 set_time_scale(time, new_time_scale);
373 double ratio = _time_scale / _last_reported_time_scale;
374 if (fabs(ratio - 1.0) > paranoid_clock_report_scale_factor) {
375 _time_scale_changed =
true;
376 _last_reported_time_scale = _time_scale;
379 _report_time_scale_time = tod + _tod_offset + keep_interval;
380 if (clock_cat.is_debug()) {
382 <<
"Will report time scale, now " << 100.0 / _time_scale
383 <<
"%, tod_age = " << tod_age <<
", time_age = " << time_age
384 <<
", ratio = " << ratio <<
"\n";
390 if (tod_age > keep_interval) {
391 while (!_timestamps.empty() &&
392 tod - _timestamps.front()._tod > keep_interval) {
393 _timestamps.pop_front();
398 _timestamps.push_back(Timestamp(time, tod));
401 double corrected_time = time * _time_scale + _time_offset;
402 double corrected_tod = tod + _tod_offset;
404 if (_time_scale_changed && corrected_tod >= _report_time_scale_time) {
405 double percent = 100.0 / _time_scale;
407 percent = floor(percent / 20.0 + 0.5) * 20.0;
409 <<
"Clock appears to be running at " << percent <<
"% real time.\n";
410 _last_reported_time_scale = _time_scale;
411 _time_scale_changed =
false;
429 switch (_chase_clock) {
431 if (corrected_time < corrected_tod) {
433 _chase_clock = CC_keep_even;
434 if (clock_cat.is_debug()) {
436 <<
"Clock back down to real time.\n";
444 double fixup = 1.0 - (1.0 / (corrected_time - corrected_tod));
445 double correction = time_delta * max(fixup, paranoid_clock_chase_factor);
446 _time_offset -= correction;
447 corrected_time -= correction;
452 if ((corrected_tod - corrected_time) > paranoid_clock_chase_threshold) {
454 _chase_clock = CC_speed_up;
456 if (clock_cat.is_debug()) {
458 <<
"Clock is behind by " << (corrected_tod - corrected_time)
459 <<
"s; speeding up to correct.\n";
461 }
else if ((corrected_time - corrected_tod) > paranoid_clock_chase_threshold) {
463 _chase_clock = CC_slow_down;
465 if (clock_cat.is_debug()) {
467 <<
"Clock is ahead by " << (corrected_time - corrected_tod)
468 <<
"s; slowing down to correct.\n";
474 if (corrected_time > corrected_tod) {
476 _chase_clock = CC_keep_even;
477 if (clock_cat.is_debug()) {
479 <<
"Clock back up to real time.\n";
487 double fixup = 1.0 - (1.0 / (corrected_tod - corrected_time));
488 double correction = time_delta * max(fixup, paranoid_clock_chase_factor);
489 _time_offset += correction;
490 corrected_time += correction;
495 if (clock_cat.is_spam()) {
497 <<
"time " << time <<
" tod " << corrected_tod
498 <<
" corrected time " << corrected_time <<
"\n";
501 return corrected_time;
512 set_time_scale(
double time,
double new_time_scale) {
513 nassertv(new_time_scale > 0.0);
514 _time_offset = time * _time_scale + _time_offset - (time * new_time_scale);
515 _time_scale = new_time_scale;
526 #include <sys/time.h>
529 static long _init_sec;
542 #ifdef GETTIMEOFDAY_ONE_PARAM
543 result = gettimeofday(&tv);
545 result = gettimeofday(&tv, (
struct timezone *)NULL);
557 return (
double)(tv.tv_sec - _init_sec) + (
double)tv.tv_usec / 1000000.0;
566 get_short_raw_time() {
571 #ifdef GETTIMEOFDAY_ONE_PARAM
572 result = gettimeofday(&tv);
574 result = gettimeofday(&tv, (
struct timezone *)NULL);
586 return (
double)(tv.tv_sec - _init_sec) + (
double)tv.tv_usec / 1000000.0;
595 set_cpu_affinity(PN_uint32 mask)
const {
610 #ifdef GETTIMEOFDAY_ONE_PARAM
611 result = gettimeofday(&tv);
613 result = gettimeofday(&tv, (
struct timezone *)NULL);
617 perror(
"gettimeofday");
620 _init_sec = tv.tv_sec;
This is a convenience class to specialize ConfigVariable as a boolean type.
An interface to whatever real-time clock we might have available in the current environment.