lltimer.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 
00034 #include "lltimer.h"
00035 
00036 #include "u64.h"
00037 
00038 #if LL_WINDOWS
00039 #       define WIN32_LEAN_AND_MEAN
00040 #       include <winsock2.h>
00041 #       include <windows.h>
00042 #elif LL_LINUX || LL_SOLARIS || LL_DARWIN
00043 #       include <errno.h>
00044 #       include <sys/time.h>
00045 #else 
00046 #       error "architecture not supported"
00047 #endif
00048 
00049 
00050 //
00051 // Locally used constants
00052 //
00053 const U32 SEC_PER_DAY = 86400;
00054 const F64 SEC_TO_MICROSEC = 1000000.f;
00055 const U64 SEC_TO_MICROSEC_U64 = 1000000;
00056 const F64 USEC_TO_SEC_F64 = 0.000001;
00057 
00058 
00059 //---------------------------------------------------------------------------
00060 // Globals and statics
00061 //---------------------------------------------------------------------------
00062 
00063 S32 gUTCOffset = 0; // viewer's offset from server UTC, in seconds
00064 LLTimer* LLTimer::sTimer = NULL;
00065 
00066 F64 gClockFrequency = 0.0;
00067 F64 gClockFrequencyInv = 0.0;
00068 F64 gClocksToMicroseconds = 0.0;
00069 U64 gTotalTimeClockCount = 0;
00070 U64 gLastTotalTimeClockCount = 0;
00071 
00072 //
00073 // Forward declarations
00074 //
00075 
00076 
00077 //---------------------------------------------------------------------------
00078 // Implementation
00079 //---------------------------------------------------------------------------
00080 
00081 #if LL_WINDOWS
00082 void ms_sleep(U32 ms)
00083 {
00084         Sleep(ms);
00085 }
00086 #elif LL_LINUX || LL_SOLARIS || LL_DARWIN
00087 void ms_sleep(U32 ms)
00088 {
00089         long mslong = ms; // tv_nsec is a long
00090         struct timespec thiswait, nextwait;
00091         bool sleep_more = false;
00092 
00093         thiswait.tv_sec = ms / 1000;
00094         thiswait.tv_nsec = (mslong % 1000) * 1000000l;
00095         do {
00096                 int result = nanosleep(&thiswait, &nextwait);
00097 
00098                 // check if sleep was interrupted by a signal; unslept
00099                 // remainder was written back into 't' and we just nanosleep
00100                 // again.
00101                 sleep_more = (result == -1 && EINTR == errno);
00102 
00103                 if (sleep_more)
00104                 {
00105                         if ( nextwait.tv_sec > thiswait.tv_sec ||
00106                              (nextwait.tv_sec == thiswait.tv_sec &&
00107                               nextwait.tv_nsec >= thiswait.tv_nsec) )
00108                         {
00109                                 // if the remaining time isn't actually going
00110                                 // down then we're being shafted by low clock
00111                                 // resolution - manually massage the sleep time
00112                                 // downward.
00113                                 if (nextwait.tv_nsec > 1000000) {
00114                                         // lose 1ms
00115                                         nextwait.tv_nsec -= 1000000;
00116                                 } else {
00117                                         if (nextwait.tv_sec == 0) {
00118                                                 // already so close to finished
00119                                                 sleep_more = false;
00120                                         } else {
00121                                                 // lose up to 1ms
00122                                                 nextwait.tv_nsec = 0;
00123                                         }
00124                                 }
00125                         }
00126                         thiswait = nextwait;
00127                 }
00128         } while (sleep_more);
00129 }
00130 #else
00131 # error "architecture not supported"
00132 #endif
00133 
00134 //
00135 // CPU clock/other clock frequency and count functions
00136 //
00137 
00138 #if LL_WINDOWS
00139 U64 get_clock_count()
00140 {
00141         static bool firstTime = true;
00142         static U64 offset;
00143                 // ensures that callers to this function never have to deal with wrap
00144 
00145         // QueryPerformanceCounter implementation
00146         LARGE_INTEGER clock_count;
00147         QueryPerformanceCounter(&clock_count);
00148         if (firstTime) {
00149                 offset = clock_count.QuadPart;
00150                 firstTime = false;
00151         }
00152         return clock_count.QuadPart - offset;
00153 }
00154 
00155 F64 calc_clock_frequency(U32 uiMeasureMSecs)
00156 {
00157         __int64 freq;
00158         QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
00159         return (F64)freq;
00160 }
00161 #endif // LL_WINDOWS
00162 
00163 
00164 #if LL_LINUX || LL_DARWIN || LL_SOLARIS
00165 // Both Linux and Mac use gettimeofday for accurate time
00166 F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
00167 {
00168         return 1000000.0; // microseconds, so 1 Mhz.
00169 }
00170 
00171 U64 get_clock_count()
00172 {
00173         // Linux clocks are in microseconds
00174         struct timeval tv;
00175         gettimeofday(&tv, NULL);
00176         return tv.tv_sec*SEC_TO_MICROSEC_U64 + tv.tv_usec;
00177 }
00178 #endif
00179 
00180 
00181 void update_clock_frequencies()
00182 {
00183         gClockFrequency = calc_clock_frequency(50U);
00184         gClockFrequencyInv = 1.0/gClockFrequency;
00185         gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
00186 }
00187 
00188 
00190 
00191 // returns a U64 number that represents the number of 
00192 // microseconds since the unix epoch - Jan 1, 1970
00193 U64 totalTime()
00194 {
00195         U64 current_clock_count = get_clock_count();
00196         if (!gTotalTimeClockCount)
00197         {
00198                 update_clock_frequencies();
00199                 gTotalTimeClockCount = current_clock_count;
00200 
00201 #if LL_WINDOWS
00202                 // Synch us up with local time (even though we PROBABLY don't need to, this is how it was implemented)
00203                 // Unix platforms use gettimeofday so they are synced, although this probably isn't a good assumption to
00204                 // make in the future.
00205 
00206                 gTotalTimeClockCount = (U64)(time(NULL) * gClockFrequency);
00207 #endif
00208 
00209                 // Update the last clock count
00210                 gLastTotalTimeClockCount = current_clock_count;
00211         }
00212         else
00213         {
00214                 if (current_clock_count >= gLastTotalTimeClockCount)
00215                 {
00216                         // No wrapping, we're all okay.
00217                         gTotalTimeClockCount += current_clock_count - gLastTotalTimeClockCount;
00218                 }
00219                 else
00220                 {
00221                         // We've wrapped.  Compensate correctly
00222                         gTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - gLastTotalTimeClockCount) + current_clock_count;
00223                 }
00224 
00225                 // Update the last clock count
00226                 gLastTotalTimeClockCount = current_clock_count;
00227         }
00228 
00229         // Return the total clock tick count in microseconds.
00230         return (U64)(gTotalTimeClockCount*gClocksToMicroseconds);
00231 }
00232 
00233 
00235 
00236 LLTimer::LLTimer()
00237 {
00238         if (!gClockFrequency)
00239         {
00240                 update_clock_frequencies();
00241         }
00242 
00243         mStarted = TRUE;
00244         reset();
00245 }
00246 
00247 LLTimer::~LLTimer()
00248 {
00249 }
00250 
00251 // static
00252 U64 LLTimer::getTotalTime()
00253 {
00254         // simply call into the implementation function.
00255         return totalTime();
00256 }       
00257 
00258 // static
00259 F64 LLTimer::getTotalSeconds()
00260 {
00261         return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
00262 }
00263 
00264 void LLTimer::reset()
00265 {
00266         mLastClockCount = get_clock_count();
00267         mExpirationTicks = 0;
00268 }
00269 
00271 
00272 U64 LLTimer::getCurrentClockCount()
00273 {
00274         return get_clock_count();
00275 }
00276 
00278 
00279 void LLTimer::setLastClockCount(U64 current_count)
00280 {
00281         mLastClockCount = current_count;
00282 }
00283 
00285 
00286 static
00287 U64 getElapsedTimeAndUpdate(U64& lastClockCount)
00288 {
00289         U64 current_clock_count = get_clock_count();
00290         U64 result;
00291 
00292         if (current_clock_count >= lastClockCount)
00293         {
00294                 result = current_clock_count - lastClockCount;
00295         }
00296         else
00297         {
00298                 // time has gone backward
00299                 result = 0;
00300         }
00301 
00302         lastClockCount = current_clock_count;
00303 
00304         return result;
00305 }
00306 
00307 
00308 F64 LLTimer::getElapsedTimeF64() const
00309 {
00310         U64 last = mLastClockCount;
00311         return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
00312 }
00313 
00314 F32 LLTimer::getElapsedTimeF32() const
00315 {
00316         return (F32)getElapsedTimeF64();
00317 }
00318 
00319 F64 LLTimer::getElapsedTimeAndResetF64()
00320 {
00321         return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
00322 }
00323 
00324 F32 LLTimer::getElapsedTimeAndResetF32()
00325 {
00326         return (F32)getElapsedTimeAndResetF64();
00327 }
00328 
00330 
00331 void  LLTimer::setTimerExpirySec(F32 expiration)
00332 {
00333         mExpirationTicks = get_clock_count()
00334                 + (U64)((F32)(expiration * gClockFrequency));
00335 }
00336 
00337 F32 LLTimer::getRemainingTimeF32() const
00338 {
00339         U64 cur_ticks = get_clock_count();
00340         if (cur_ticks > mExpirationTicks)
00341         {
00342                 return 0.0f;
00343         }
00344         return F32((mExpirationTicks - cur_ticks) * gClockFrequencyInv);
00345 }
00346 
00347 
00348 BOOL  LLTimer::checkExpirationAndReset(F32 expiration)
00349 {
00350         U64 cur_ticks = get_clock_count();
00351         if (cur_ticks < mExpirationTicks)
00352         {
00353                 return FALSE;
00354         }
00355 
00356         mExpirationTicks = cur_ticks
00357                 + (U64)((F32)(expiration * gClockFrequency));
00358         return TRUE;
00359 }
00360 
00361 
00362 BOOL  LLTimer::hasExpired() const
00363 {
00364         return (get_clock_count() >= mExpirationTicks)
00365                 ? TRUE : FALSE;
00366 }
00367 
00369 
00370 BOOL LLTimer::knownBadTimer()
00371 {
00372         BOOL failed = FALSE;
00373 
00374 #if LL_WINDOWS
00375         WCHAR bad_pci_list[][10] = {L"1039:0530",
00376                                                         L"1039:0620",
00377                                                             L"10B9:0533",
00378                                                             L"10B9:1533",
00379                                                             L"1106:0596",
00380                                                             L"1106:0686",
00381                                                             L"1166:004F",
00382                                                             L"1166:0050",
00383                                                             L"8086:7110",
00384                                                             L"\0"
00385         };
00386 
00387         HKEY hKey = NULL;
00388         LONG nResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SYSTEM\\CurrentControlSet\\Enum\\PCI", 0,
00389                                                                   KEY_EXECUTE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey);
00390         
00391         WCHAR name[1024];
00392         DWORD name_len = 1024;
00393         FILETIME scrap;
00394 
00395         S32 key_num = 0;
00396         WCHAR pci_id[10];
00397 
00398         wcscpy(pci_id, L"0000:0000");    /*Flawfinder: ignore*/
00399 
00400         while (nResult == ERROR_SUCCESS)
00401         {
00402                 nResult = ::RegEnumKeyEx(hKey, key_num++, name, &name_len, NULL, NULL, NULL, &scrap);
00403 
00404                 if (nResult == ERROR_SUCCESS)
00405                 {
00406                         memcpy(&pci_id[0],&name[4],4);          /* Flawfinder: ignore */
00407                         memcpy(&pci_id[5],&name[13],4);         /* Flawfinder: ignore */
00408 
00409                         for (S32 check = 0; bad_pci_list[check][0]; check++)
00410                         {
00411                                 if (!wcscmp(pci_id, bad_pci_list[check]))
00412                                 {
00413 //                                      llwarns << "unreliable PCI chipset found!! " << pci_id << endl;
00414                                         failed = TRUE;
00415                                         break;
00416                                 }
00417                         }
00418 //                      llinfo << "PCI chipset found: " << pci_id << endl;
00419                         name_len = 1024;
00420                 }
00421         }
00422 #endif
00423         return(failed);
00424 }
00425 
00427 // 
00428 // NON-MEMBER FUNCTIONS
00429 //
00431 
00432 U32 time_corrected()
00433 {
00434         U32 corrected_time = (U32)time(NULL) + gUTCOffset;
00435         return corrected_time;
00436 }
00437 
00438 
00439 // Is the current computer (in its current time zone)
00440 // observing daylight savings time?
00441 BOOL is_daylight_savings()
00442 {
00443         time_t now = time(NULL);
00444 
00445         // Internal buffer to local server time
00446         struct tm* internal_time = localtime(&now);
00447 
00448         // tm_isdst > 0  =>  daylight savings
00449         // tm_isdst = 0  =>  not daylight savings
00450         // tm_isdst < 0  =>  can't tell
00451         return (internal_time->tm_isdst > 0);
00452 }
00453 
00454 
00455 struct tm* utc_to_pacific_time(S32 utc_time, BOOL pacific_daylight_time)
00456 {
00457         time_t unix_time = (time_t)utc_time;
00458 
00459         S32 pacific_offset_hours;
00460         if (pacific_daylight_time)
00461         {
00462                 pacific_offset_hours = -7;
00463         }
00464         else
00465         {
00466                 pacific_offset_hours = -8;
00467         }
00468 
00469         // We subtract off the PST/PDT offset _before_ getting
00470         // "UTC" time, because this will handle wrapping around
00471         // for 5 AM UTC -> 10 PM PDT of the previous day.
00472         unix_time += pacific_offset_hours * MIN_PER_HOUR * SEC_PER_MIN;
00473  
00474         // Internal buffer to PST/PDT (see above)
00475         struct tm* internal_time = gmtime(&unix_time);
00476 
00477         /*
00478         // Don't do this, this won't correctly tell you if daylight savings is active in CA or not.
00479         if (pacific_daylight_time)
00480         {
00481                 internal_time->tm_isdst = 1;
00482         }
00483         */
00484 
00485         return internal_time;
00486 }
00487 
00488 
00489 void microsecondsToTimecodeString(U64 current_time, char *tcstring)
00490 {
00491         U64 hours;
00492         U64 minutes;
00493         U64 seconds;
00494         U64 frames;
00495         U64 subframes;
00496 
00497         hours = current_time / (U64)3600000000ul;
00498         minutes = current_time / (U64)60000000;
00499         minutes %= 60;
00500         seconds = current_time / (U64)1000000;
00501         seconds %= 60;
00502         frames = current_time / (U64)41667;
00503         frames %= 24;
00504         subframes = current_time / (U64)42;
00505         subframes %= 100;
00506 
00507         sprintf(tcstring,"%3.3d:%2.2d:%2.2d:%2.2d.%2.2d",(int)hours,(int)minutes,(int)seconds,(int)frames,(int)subframes);              /* Flawfinder: ignore */
00508 }
00509 
00510 
00511 void secondsToTimecodeString(F32 current_time, char *tcstring)
00512 {
00513         microsecondsToTimecodeString((U64)((F64)(SEC_TO_MICROSEC*current_time)), tcstring);
00514 }
00515 
00516 
00518 //
00519 //              LLEventTimer Implementation
00520 //
00522 
00523 std::list<LLEventTimer*> LLEventTimer::sActiveList;
00524 
00525 LLEventTimer::LLEventTimer(F32 period)
00526 : mEventTimer()
00527 {
00528         mPeriod = period;
00529         sActiveList.push_back(this);
00530 }
00531 
00532 LLEventTimer::~LLEventTimer() 
00533 {
00534         sActiveList.remove(this);
00535 }
00536 
00537 void LLEventTimer::updateClass() 
00538 {
00539         std::list<LLEventTimer*> completed_timers;
00540         for (std::list<LLEventTimer*>::iterator iter = sActiveList.begin(); iter != sActiveList.end(); ) 
00541         {
00542                 LLEventTimer* timer = *iter++;
00543                 F32 et = timer->mEventTimer.getElapsedTimeF32();
00544                 if (et > timer->mPeriod) {
00545                         timer->mEventTimer.reset();
00546                         if ( timer->tick() )
00547                         {
00548                                 completed_timers.push_back( timer );
00549                         }
00550                 }
00551         }
00552 
00553         if ( completed_timers.size() > 0 )
00554         {
00555                 for (std::list<LLEventTimer*>::iterator completed_iter = completed_timers.begin(); 
00556                          completed_iter != completed_timers.end(); 
00557                          completed_iter++ ) 
00558                 {
00559                         delete *completed_iter;
00560                 }
00561         }
00562 }
00563 
00564 

Generated on Fri May 16 08:32:09 2008 for SecondLife by  doxygen 1.5.5