Panda3D

trueClock.cxx

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
 All Classes Functions Variables Enumerations