PageRenderTime 62ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llcommon/lltimer.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 551 lines | 350 code | 99 blank | 102 comment | 35 complexity | 1d76e3872548eff7f6303c0fc6a8ea2c MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lltimer.cpp
  3. * @brief Cross-platform objects for doing timing
  4. *
  5. * $LicenseInfo:firstyear=2000&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "lltimer.h"
  28. #include "u64.h"
  29. #if LL_WINDOWS
  30. # define WIN32_LEAN_AND_MEAN
  31. # include <winsock2.h>
  32. # include <windows.h>
  33. #elif LL_LINUX || LL_SOLARIS || LL_DARWIN
  34. # include <errno.h>
  35. # include <sys/time.h>
  36. #else
  37. # error "architecture not supported"
  38. #endif
  39. //
  40. // Locally used constants
  41. //
  42. const U32 SEC_PER_DAY = 86400;
  43. const F64 SEC_TO_MICROSEC = 1000000.f;
  44. const U64 SEC_TO_MICROSEC_U64 = 1000000;
  45. const F64 USEC_TO_SEC_F64 = 0.000001;
  46. //---------------------------------------------------------------------------
  47. // Globals and statics
  48. //---------------------------------------------------------------------------
  49. S32 gUTCOffset = 0; // viewer's offset from server UTC, in seconds
  50. LLTimer* LLTimer::sTimer = NULL;
  51. F64 gClockFrequency = 0.0;
  52. F64 gClockFrequencyInv = 0.0;
  53. F64 gClocksToMicroseconds = 0.0;
  54. U64 gTotalTimeClockCount = 0;
  55. U64 gLastTotalTimeClockCount = 0;
  56. //
  57. // Forward declarations
  58. //
  59. //---------------------------------------------------------------------------
  60. // Implementation
  61. //---------------------------------------------------------------------------
  62. #if LL_WINDOWS
  63. void ms_sleep(U32 ms)
  64. {
  65. Sleep(ms);
  66. }
  67. U32 micro_sleep(U64 us, U32 max_yields)
  68. {
  69. // max_yields is unused; just fiddle with it to avoid warnings.
  70. max_yields = 0;
  71. ms_sleep(us / 1000);
  72. return 0;
  73. }
  74. #elif LL_LINUX || LL_SOLARIS || LL_DARWIN
  75. static void _sleep_loop(struct timespec& thiswait)
  76. {
  77. struct timespec nextwait;
  78. bool sleep_more = false;
  79. do {
  80. int result = nanosleep(&thiswait, &nextwait);
  81. // check if sleep was interrupted by a signal; unslept
  82. // remainder was written back into 't' and we just nanosleep
  83. // again.
  84. sleep_more = (result == -1 && EINTR == errno);
  85. if (sleep_more)
  86. {
  87. if ( nextwait.tv_sec > thiswait.tv_sec ||
  88. (nextwait.tv_sec == thiswait.tv_sec &&
  89. nextwait.tv_nsec >= thiswait.tv_nsec) )
  90. {
  91. // if the remaining time isn't actually going
  92. // down then we're being shafted by low clock
  93. // resolution - manually massage the sleep time
  94. // downward.
  95. if (nextwait.tv_nsec > 1000000) {
  96. // lose 1ms
  97. nextwait.tv_nsec -= 1000000;
  98. } else {
  99. if (nextwait.tv_sec == 0) {
  100. // already so close to finished
  101. sleep_more = false;
  102. } else {
  103. // lose up to 1ms
  104. nextwait.tv_nsec = 0;
  105. }
  106. }
  107. }
  108. thiswait = nextwait;
  109. }
  110. } while (sleep_more);
  111. }
  112. U32 micro_sleep(U64 us, U32 max_yields)
  113. {
  114. U64 start = get_clock_count();
  115. // This is kernel dependent. Currently, our kernel generates software clock
  116. // interrupts at 250 Hz (every 4,000 microseconds).
  117. const U64 KERNEL_SLEEP_INTERVAL_US = 4000;
  118. S32 num_sleep_intervals = (us - (KERNEL_SLEEP_INTERVAL_US >> 1)) / KERNEL_SLEEP_INTERVAL_US;
  119. if (num_sleep_intervals > 0)
  120. {
  121. U64 sleep_time = (num_sleep_intervals * KERNEL_SLEEP_INTERVAL_US) - (KERNEL_SLEEP_INTERVAL_US >> 1);
  122. struct timespec thiswait;
  123. thiswait.tv_sec = sleep_time / 1000000;
  124. thiswait.tv_nsec = (sleep_time % 1000000) * 1000l;
  125. _sleep_loop(thiswait);
  126. }
  127. U64 current_clock = get_clock_count();
  128. U32 yields = 0;
  129. while ( (yields < max_yields)
  130. && (current_clock - start < us) )
  131. {
  132. sched_yield();
  133. ++yields;
  134. current_clock = get_clock_count();
  135. }
  136. return yields;
  137. }
  138. void ms_sleep(U32 ms)
  139. {
  140. long mslong = ms; // tv_nsec is a long
  141. struct timespec thiswait;
  142. thiswait.tv_sec = ms / 1000;
  143. thiswait.tv_nsec = (mslong % 1000) * 1000000l;
  144. _sleep_loop(thiswait);
  145. }
  146. #else
  147. # error "architecture not supported"
  148. #endif
  149. //
  150. // CPU clock/other clock frequency and count functions
  151. //
  152. #if LL_WINDOWS
  153. U64 get_clock_count()
  154. {
  155. static bool firstTime = true;
  156. static U64 offset;
  157. // ensures that callers to this function never have to deal with wrap
  158. // QueryPerformanceCounter implementation
  159. LARGE_INTEGER clock_count;
  160. QueryPerformanceCounter(&clock_count);
  161. if (firstTime) {
  162. offset = clock_count.QuadPart;
  163. firstTime = false;
  164. }
  165. return clock_count.QuadPart - offset;
  166. }
  167. F64 calc_clock_frequency(U32 uiMeasureMSecs)
  168. {
  169. __int64 freq;
  170. QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
  171. return (F64)freq;
  172. }
  173. #endif // LL_WINDOWS
  174. #if LL_LINUX || LL_DARWIN || LL_SOLARIS
  175. // Both Linux and Mac use gettimeofday for accurate time
  176. F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
  177. {
  178. return 1000000.0; // microseconds, so 1 MHz.
  179. }
  180. U64 get_clock_count()
  181. {
  182. // Linux clocks are in microseconds
  183. struct timeval tv;
  184. gettimeofday(&tv, NULL);
  185. return tv.tv_sec*SEC_TO_MICROSEC_U64 + tv.tv_usec;
  186. }
  187. #endif
  188. void update_clock_frequencies()
  189. {
  190. gClockFrequency = calc_clock_frequency(50U);
  191. gClockFrequencyInv = 1.0/gClockFrequency;
  192. gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
  193. }
  194. ///////////////////////////////////////////////////////////////////////////////
  195. // returns a U64 number that represents the number of
  196. // microseconds since the unix epoch - Jan 1, 1970
  197. U64 totalTime()
  198. {
  199. U64 current_clock_count = get_clock_count();
  200. if (!gTotalTimeClockCount)
  201. {
  202. update_clock_frequencies();
  203. gTotalTimeClockCount = current_clock_count;
  204. #if LL_WINDOWS
  205. // Synch us up with local time (even though we PROBABLY don't need to, this is how it was implemented)
  206. // Unix platforms use gettimeofday so they are synced, although this probably isn't a good assumption to
  207. // make in the future.
  208. gTotalTimeClockCount = (U64)(time(NULL) * gClockFrequency);
  209. #endif
  210. // Update the last clock count
  211. gLastTotalTimeClockCount = current_clock_count;
  212. }
  213. else
  214. {
  215. if (current_clock_count >= gLastTotalTimeClockCount)
  216. {
  217. // No wrapping, we're all okay.
  218. gTotalTimeClockCount += current_clock_count - gLastTotalTimeClockCount;
  219. }
  220. else
  221. {
  222. // We've wrapped. Compensate correctly
  223. gTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - gLastTotalTimeClockCount) + current_clock_count;
  224. }
  225. // Update the last clock count
  226. gLastTotalTimeClockCount = current_clock_count;
  227. }
  228. // Return the total clock tick count in microseconds.
  229. return (U64)(gTotalTimeClockCount*gClocksToMicroseconds);
  230. }
  231. ///////////////////////////////////////////////////////////////////////////////
  232. LLTimer::LLTimer()
  233. {
  234. if (!gClockFrequency)
  235. {
  236. update_clock_frequencies();
  237. }
  238. mStarted = TRUE;
  239. reset();
  240. }
  241. LLTimer::~LLTimer()
  242. {
  243. }
  244. // static
  245. U64 LLTimer::getTotalTime()
  246. {
  247. // simply call into the implementation function.
  248. return totalTime();
  249. }
  250. // static
  251. F64 LLTimer::getTotalSeconds()
  252. {
  253. return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
  254. }
  255. void LLTimer::reset()
  256. {
  257. mLastClockCount = get_clock_count();
  258. mExpirationTicks = 0;
  259. }
  260. ///////////////////////////////////////////////////////////////////////////////
  261. U64 LLTimer::getCurrentClockCount()
  262. {
  263. return get_clock_count();
  264. }
  265. ///////////////////////////////////////////////////////////////////////////////
  266. void LLTimer::setLastClockCount(U64 current_count)
  267. {
  268. mLastClockCount = current_count;
  269. }
  270. ///////////////////////////////////////////////////////////////////////////////
  271. static
  272. U64 getElapsedTimeAndUpdate(U64& lastClockCount)
  273. {
  274. U64 current_clock_count = get_clock_count();
  275. U64 result;
  276. if (current_clock_count >= lastClockCount)
  277. {
  278. result = current_clock_count - lastClockCount;
  279. }
  280. else
  281. {
  282. // time has gone backward
  283. result = 0;
  284. }
  285. lastClockCount = current_clock_count;
  286. return result;
  287. }
  288. F64 LLTimer::getElapsedTimeF64() const
  289. {
  290. U64 last = mLastClockCount;
  291. return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
  292. }
  293. F32 LLTimer::getElapsedTimeF32() const
  294. {
  295. return (F32)getElapsedTimeF64();
  296. }
  297. F64 LLTimer::getElapsedTimeAndResetF64()
  298. {
  299. return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
  300. }
  301. F32 LLTimer::getElapsedTimeAndResetF32()
  302. {
  303. return (F32)getElapsedTimeAndResetF64();
  304. }
  305. ///////////////////////////////////////////////////////////////////////////////
  306. void LLTimer::setTimerExpirySec(F32 expiration)
  307. {
  308. mExpirationTicks = get_clock_count()
  309. + (U64)((F32)(expiration * gClockFrequency));
  310. }
  311. F32 LLTimer::getRemainingTimeF32() const
  312. {
  313. U64 cur_ticks = get_clock_count();
  314. if (cur_ticks > mExpirationTicks)
  315. {
  316. return 0.0f;
  317. }
  318. return F32((mExpirationTicks - cur_ticks) * gClockFrequencyInv);
  319. }
  320. BOOL LLTimer::checkExpirationAndReset(F32 expiration)
  321. {
  322. U64 cur_ticks = get_clock_count();
  323. if (cur_ticks < mExpirationTicks)
  324. {
  325. return FALSE;
  326. }
  327. mExpirationTicks = cur_ticks
  328. + (U64)((F32)(expiration * gClockFrequency));
  329. return TRUE;
  330. }
  331. BOOL LLTimer::hasExpired() const
  332. {
  333. return (get_clock_count() >= mExpirationTicks)
  334. ? TRUE : FALSE;
  335. }
  336. ///////////////////////////////////////////////////////////////////////////////
  337. BOOL LLTimer::knownBadTimer()
  338. {
  339. BOOL failed = FALSE;
  340. #if LL_WINDOWS
  341. WCHAR bad_pci_list[][10] = {L"1039:0530",
  342. L"1039:0620",
  343. L"10B9:0533",
  344. L"10B9:1533",
  345. L"1106:0596",
  346. L"1106:0686",
  347. L"1166:004F",
  348. L"1166:0050",
  349. L"8086:7110",
  350. L"\0"
  351. };
  352. HKEY hKey = NULL;
  353. LONG nResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SYSTEM\\CurrentControlSet\\Enum\\PCI", 0,
  354. KEY_EXECUTE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey);
  355. WCHAR name[1024];
  356. DWORD name_len = 1024;
  357. FILETIME scrap;
  358. S32 key_num = 0;
  359. WCHAR pci_id[10];
  360. wcscpy(pci_id, L"0000:0000"); /*Flawfinder: ignore*/
  361. while (nResult == ERROR_SUCCESS)
  362. {
  363. nResult = ::RegEnumKeyEx(hKey, key_num++, name, &name_len, NULL, NULL, NULL, &scrap);
  364. if (nResult == ERROR_SUCCESS)
  365. {
  366. memcpy(&pci_id[0],&name[4],4); /* Flawfinder: ignore */
  367. memcpy(&pci_id[5],&name[13],4); /* Flawfinder: ignore */
  368. for (S32 check = 0; bad_pci_list[check][0]; check++)
  369. {
  370. if (!wcscmp(pci_id, bad_pci_list[check]))
  371. {
  372. // llwarns << "unreliable PCI chipset found!! " << pci_id << endl;
  373. failed = TRUE;
  374. break;
  375. }
  376. }
  377. // llinfo << "PCI chipset found: " << pci_id << endl;
  378. name_len = 1024;
  379. }
  380. }
  381. #endif
  382. return(failed);
  383. }
  384. ///////////////////////////////////////////////////////////////////////////////
  385. //
  386. // NON-MEMBER FUNCTIONS
  387. //
  388. ///////////////////////////////////////////////////////////////////////////////
  389. time_t time_corrected()
  390. {
  391. return time(NULL) + gUTCOffset;
  392. }
  393. // Is the current computer (in its current time zone)
  394. // observing daylight savings time?
  395. BOOL is_daylight_savings()
  396. {
  397. time_t now = time(NULL);
  398. // Internal buffer to local server time
  399. struct tm* internal_time = localtime(&now);
  400. // tm_isdst > 0 => daylight savings
  401. // tm_isdst = 0 => not daylight savings
  402. // tm_isdst < 0 => can't tell
  403. return (internal_time->tm_isdst > 0);
  404. }
  405. struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time)
  406. {
  407. S32 pacific_offset_hours;
  408. if (pacific_daylight_time)
  409. {
  410. pacific_offset_hours = 7;
  411. }
  412. else
  413. {
  414. pacific_offset_hours = 8;
  415. }
  416. // We subtract off the PST/PDT offset _before_ getting
  417. // "UTC" time, because this will handle wrapping around
  418. // for 5 AM UTC -> 10 PM PDT of the previous day.
  419. utc_time -= pacific_offset_hours * MIN_PER_HOUR * SEC_PER_MIN;
  420. // Internal buffer to PST/PDT (see above)
  421. struct tm* internal_time = gmtime(&utc_time);
  422. /*
  423. // Don't do this, this won't correctly tell you if daylight savings is active in CA or not.
  424. if (pacific_daylight_time)
  425. {
  426. internal_time->tm_isdst = 1;
  427. }
  428. */
  429. return internal_time;
  430. }
  431. void microsecondsToTimecodeString(U64 current_time, std::string& tcstring)
  432. {
  433. U64 hours;
  434. U64 minutes;
  435. U64 seconds;
  436. U64 frames;
  437. U64 subframes;
  438. hours = current_time / (U64)3600000000ul;
  439. minutes = current_time / (U64)60000000;
  440. minutes %= 60;
  441. seconds = current_time / (U64)1000000;
  442. seconds %= 60;
  443. frames = current_time / (U64)41667;
  444. frames %= 24;
  445. subframes = current_time / (U64)42;
  446. subframes %= 100;
  447. tcstring = llformat("%3.3d:%2.2d:%2.2d:%2.2d.%2.2d",(int)hours,(int)minutes,(int)seconds,(int)frames,(int)subframes);
  448. }
  449. void secondsToTimecodeString(F32 current_time, std::string& tcstring)
  450. {
  451. microsecondsToTimecodeString((U64)((F64)(SEC_TO_MICROSEC*current_time)), tcstring);
  452. }