lltimer.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:13k
源码类别:

游戏引擎

开发平台:

C++ Builder

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