PageRenderTime 107ms CodeModel.GetById 58ms app.highlight 44ms RepoModel.GetById 1ms 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
  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
 27#include "linden_common.h"
 28
 29#include "lltimer.h"
 30
 31#include "u64.h"
 32
 33#if LL_WINDOWS
 34#	define WIN32_LEAN_AND_MEAN
 35#	include <winsock2.h>
 36#	include <windows.h>
 37#elif LL_LINUX || LL_SOLARIS || LL_DARWIN
 38#       include <errno.h>
 39#	include <sys/time.h>
 40#else 
 41#	error "architecture not supported"
 42#endif
 43
 44
 45//
 46// Locally used constants
 47//
 48const U32 SEC_PER_DAY = 86400;
 49const F64 SEC_TO_MICROSEC = 1000000.f;
 50const U64 SEC_TO_MICROSEC_U64 = 1000000;
 51const F64 USEC_TO_SEC_F64 = 0.000001;
 52
 53
 54//---------------------------------------------------------------------------
 55// Globals and statics
 56//---------------------------------------------------------------------------
 57
 58S32 gUTCOffset = 0; // viewer's offset from server UTC, in seconds
 59LLTimer* LLTimer::sTimer = NULL;
 60
 61F64 gClockFrequency = 0.0;
 62F64 gClockFrequencyInv = 0.0;
 63F64 gClocksToMicroseconds = 0.0;
 64U64 gTotalTimeClockCount = 0;
 65U64 gLastTotalTimeClockCount = 0;
 66
 67//
 68// Forward declarations
 69//
 70
 71
 72//---------------------------------------------------------------------------
 73// Implementation
 74//---------------------------------------------------------------------------
 75
 76#if LL_WINDOWS
 77void ms_sleep(U32 ms)
 78{
 79	Sleep(ms);
 80}
 81
 82U32 micro_sleep(U64 us, U32 max_yields)
 83{
 84    // max_yields is unused; just fiddle with it to avoid warnings.
 85    max_yields = 0;
 86    ms_sleep(us / 1000);
 87    return 0;
 88}
 89#elif LL_LINUX || LL_SOLARIS || LL_DARWIN
 90static void _sleep_loop(struct timespec& thiswait)
 91{
 92	struct timespec nextwait;
 93	bool sleep_more = false;
 94
 95	do {
 96		int result = nanosleep(&thiswait, &nextwait);
 97
 98		// check if sleep was interrupted by a signal; unslept
 99		// remainder was written back into 't' and we just nanosleep
100		// again.
101		sleep_more = (result == -1 && EINTR == errno);
102
103		if (sleep_more)
104		{
105			if ( nextwait.tv_sec > thiswait.tv_sec ||
106			     (nextwait.tv_sec == thiswait.tv_sec &&
107			      nextwait.tv_nsec >= thiswait.tv_nsec) )
108			{
109				// if the remaining time isn't actually going
110				// down then we're being shafted by low clock
111				// resolution - manually massage the sleep time
112				// downward.
113				if (nextwait.tv_nsec > 1000000) {
114					// lose 1ms
115					nextwait.tv_nsec -= 1000000;
116				} else {
117					if (nextwait.tv_sec == 0) {
118						// already so close to finished
119						sleep_more = false;
120					} else {
121						// lose up to 1ms
122						nextwait.tv_nsec = 0;
123					}
124				}
125			}
126			thiswait = nextwait;
127		}
128	} while (sleep_more);
129}
130
131U32 micro_sleep(U64 us, U32 max_yields)
132{
133    U64 start = get_clock_count();
134    // This is kernel dependent.  Currently, our kernel generates software clock
135    // interrupts at 250 Hz (every 4,000 microseconds).
136    const U64 KERNEL_SLEEP_INTERVAL_US = 4000;
137
138    S32 num_sleep_intervals = (us - (KERNEL_SLEEP_INTERVAL_US >> 1)) / KERNEL_SLEEP_INTERVAL_US;
139    if (num_sleep_intervals > 0)
140    {
141        U64 sleep_time = (num_sleep_intervals * KERNEL_SLEEP_INTERVAL_US) - (KERNEL_SLEEP_INTERVAL_US >> 1);
142        struct timespec thiswait;
143        thiswait.tv_sec = sleep_time / 1000000;
144        thiswait.tv_nsec = (sleep_time % 1000000) * 1000l;
145        _sleep_loop(thiswait);
146    }
147
148    U64 current_clock = get_clock_count();
149    U32 yields = 0;
150    while (    (yields < max_yields)
151            && (current_clock - start < us) )
152    {
153        sched_yield();
154        ++yields;
155        current_clock = get_clock_count();
156    }
157    return yields;
158}
159
160void ms_sleep(U32 ms)
161{
162	long mslong = ms; // tv_nsec is a long
163	struct timespec thiswait;
164	thiswait.tv_sec = ms / 1000;
165	thiswait.tv_nsec = (mslong % 1000) * 1000000l;
166    _sleep_loop(thiswait);
167}
168#else
169# error "architecture not supported"
170#endif
171
172//
173// CPU clock/other clock frequency and count functions
174//
175
176#if LL_WINDOWS
177U64 get_clock_count()
178{
179	static bool firstTime = true;
180	static U64 offset;
181		// ensures that callers to this function never have to deal with wrap
182
183	// QueryPerformanceCounter implementation
184	LARGE_INTEGER clock_count;
185	QueryPerformanceCounter(&clock_count);
186	if (firstTime) {
187		offset = clock_count.QuadPart;
188		firstTime = false;
189	}
190	return clock_count.QuadPart - offset;
191}
192
193F64 calc_clock_frequency(U32 uiMeasureMSecs)
194{
195	__int64 freq;
196	QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
197	return (F64)freq;
198}
199#endif // LL_WINDOWS
200
201
202#if LL_LINUX || LL_DARWIN || LL_SOLARIS
203// Both Linux and Mac use gettimeofday for accurate time
204F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
205{
206	return 1000000.0; // microseconds, so 1 MHz.
207}
208
209U64 get_clock_count()
210{
211	// Linux clocks are in microseconds
212	struct timeval tv;
213	gettimeofday(&tv, NULL);
214	return tv.tv_sec*SEC_TO_MICROSEC_U64 + tv.tv_usec;
215}
216#endif
217
218
219void update_clock_frequencies()
220{
221	gClockFrequency = calc_clock_frequency(50U);
222	gClockFrequencyInv = 1.0/gClockFrequency;
223	gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
224}
225
226
227///////////////////////////////////////////////////////////////////////////////
228
229// returns a U64 number that represents the number of 
230// microseconds since the unix epoch - Jan 1, 1970
231U64 totalTime()
232{
233	U64 current_clock_count = get_clock_count();
234	if (!gTotalTimeClockCount)
235	{
236		update_clock_frequencies();
237		gTotalTimeClockCount = current_clock_count;
238
239#if LL_WINDOWS
240		// Synch us up with local time (even though we PROBABLY don't need to, this is how it was implemented)
241		// Unix platforms use gettimeofday so they are synced, although this probably isn't a good assumption to
242		// make in the future.
243
244		gTotalTimeClockCount = (U64)(time(NULL) * gClockFrequency);
245#endif
246
247		// Update the last clock count
248		gLastTotalTimeClockCount = current_clock_count;
249	}
250	else
251	{
252		if (current_clock_count >= gLastTotalTimeClockCount)
253		{
254			// No wrapping, we're all okay.
255			gTotalTimeClockCount += current_clock_count - gLastTotalTimeClockCount;
256		}
257		else
258		{
259			// We've wrapped.  Compensate correctly
260			gTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - gLastTotalTimeClockCount) + current_clock_count;
261		}
262
263		// Update the last clock count
264		gLastTotalTimeClockCount = current_clock_count;
265	}
266
267	// Return the total clock tick count in microseconds.
268	return (U64)(gTotalTimeClockCount*gClocksToMicroseconds);
269}
270
271
272///////////////////////////////////////////////////////////////////////////////
273
274LLTimer::LLTimer()
275{
276	if (!gClockFrequency)
277	{
278		update_clock_frequencies();
279	}
280
281	mStarted = TRUE;
282	reset();
283}
284
285LLTimer::~LLTimer()
286{
287}
288
289// static
290U64 LLTimer::getTotalTime()
291{
292	// simply call into the implementation function.
293	return totalTime();
294}	
295
296// static
297F64 LLTimer::getTotalSeconds()
298{
299	return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
300}
301
302void LLTimer::reset()
303{
304	mLastClockCount = get_clock_count();
305	mExpirationTicks = 0;
306}
307
308///////////////////////////////////////////////////////////////////////////////
309
310U64 LLTimer::getCurrentClockCount()
311{
312	return get_clock_count();
313}
314
315///////////////////////////////////////////////////////////////////////////////
316
317void LLTimer::setLastClockCount(U64 current_count)
318{
319	mLastClockCount = current_count;
320}
321
322///////////////////////////////////////////////////////////////////////////////
323
324static
325U64 getElapsedTimeAndUpdate(U64& lastClockCount)
326{
327	U64 current_clock_count = get_clock_count();
328	U64 result;
329
330	if (current_clock_count >= lastClockCount)
331	{
332		result = current_clock_count - lastClockCount;
333	}
334	else
335	{
336		// time has gone backward
337		result = 0;
338	}
339
340	lastClockCount = current_clock_count;
341
342	return result;
343}
344
345
346F64 LLTimer::getElapsedTimeF64() const
347{
348	U64 last = mLastClockCount;
349	return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
350}
351
352F32 LLTimer::getElapsedTimeF32() const
353{
354	return (F32)getElapsedTimeF64();
355}
356
357F64 LLTimer::getElapsedTimeAndResetF64()
358{
359	return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
360}
361
362F32 LLTimer::getElapsedTimeAndResetF32()
363{
364	return (F32)getElapsedTimeAndResetF64();
365}
366
367///////////////////////////////////////////////////////////////////////////////
368
369void  LLTimer::setTimerExpirySec(F32 expiration)
370{
371	mExpirationTicks = get_clock_count()
372		+ (U64)((F32)(expiration * gClockFrequency));
373}
374
375F32 LLTimer::getRemainingTimeF32() const
376{
377	U64 cur_ticks = get_clock_count();
378	if (cur_ticks > mExpirationTicks)
379	{
380		return 0.0f;
381	}
382	return F32((mExpirationTicks - cur_ticks) * gClockFrequencyInv);
383}
384
385
386BOOL  LLTimer::checkExpirationAndReset(F32 expiration)
387{
388	U64 cur_ticks = get_clock_count();
389	if (cur_ticks < mExpirationTicks)
390	{
391		return FALSE;
392	}
393
394	mExpirationTicks = cur_ticks
395		+ (U64)((F32)(expiration * gClockFrequency));
396	return TRUE;
397}
398
399
400BOOL  LLTimer::hasExpired() const
401{
402	return (get_clock_count() >= mExpirationTicks)
403		? TRUE : FALSE;
404}
405
406///////////////////////////////////////////////////////////////////////////////
407
408BOOL LLTimer::knownBadTimer()
409{
410	BOOL failed = FALSE;
411
412#if LL_WINDOWS
413	WCHAR bad_pci_list[][10] = {L"1039:0530",
414						        L"1039:0620",
415							    L"10B9:0533",
416							    L"10B9:1533",
417							    L"1106:0596",
418							    L"1106:0686",
419							    L"1166:004F",
420							    L"1166:0050",
421 							    L"8086:7110",
422							    L"\0"
423	};
424
425	HKEY hKey = NULL;
426	LONG nResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SYSTEM\\CurrentControlSet\\Enum\\PCI", 0,
427								  KEY_EXECUTE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey);
428	
429	WCHAR name[1024];
430	DWORD name_len = 1024;
431	FILETIME scrap;
432
433	S32 key_num = 0;
434	WCHAR pci_id[10];
435
436	wcscpy(pci_id, L"0000:0000");	 /*Flawfinder: ignore*/
437
438	while (nResult == ERROR_SUCCESS)
439	{
440		nResult = ::RegEnumKeyEx(hKey, key_num++, name, &name_len, NULL, NULL, NULL, &scrap);
441
442		if (nResult == ERROR_SUCCESS)
443		{
444			memcpy(&pci_id[0],&name[4],4);		/* Flawfinder: ignore */
445			memcpy(&pci_id[5],&name[13],4);		/* Flawfinder: ignore */
446
447			for (S32 check = 0; bad_pci_list[check][0]; check++)
448			{
449				if (!wcscmp(pci_id, bad_pci_list[check]))
450				{
451//					llwarns << "unreliable PCI chipset found!! " << pci_id << endl;
452					failed = TRUE;
453					break;
454				}
455			}
456//			llinfo << "PCI chipset found: " << pci_id << endl;
457			name_len = 1024;
458		}
459	}
460#endif
461	return(failed);
462}
463
464///////////////////////////////////////////////////////////////////////////////
465// 
466// NON-MEMBER FUNCTIONS
467//
468///////////////////////////////////////////////////////////////////////////////
469
470time_t time_corrected()
471{
472	return time(NULL) + gUTCOffset;
473}
474
475
476// Is the current computer (in its current time zone)
477// observing daylight savings time?
478BOOL is_daylight_savings()
479{
480	time_t now = time(NULL);
481
482	// Internal buffer to local server time
483	struct tm* internal_time = localtime(&now);
484
485	// tm_isdst > 0  =>  daylight savings
486	// tm_isdst = 0  =>  not daylight savings
487	// tm_isdst < 0  =>  can't tell
488	return (internal_time->tm_isdst > 0);
489}
490
491
492struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time)
493{
494	S32 pacific_offset_hours;
495	if (pacific_daylight_time)
496	{
497		pacific_offset_hours = 7;
498	}
499	else
500	{
501		pacific_offset_hours = 8;
502	}
503
504	// We subtract off the PST/PDT offset _before_ getting
505	// "UTC" time, because this will handle wrapping around
506	// for 5 AM UTC -> 10 PM PDT of the previous day.
507	utc_time -= pacific_offset_hours * MIN_PER_HOUR * SEC_PER_MIN;
508 
509	// Internal buffer to PST/PDT (see above)
510	struct tm* internal_time = gmtime(&utc_time);
511
512	/*
513	// Don't do this, this won't correctly tell you if daylight savings is active in CA or not.
514	if (pacific_daylight_time)
515	{
516		internal_time->tm_isdst = 1;
517	}
518	*/
519
520	return internal_time;
521}
522
523
524void microsecondsToTimecodeString(U64 current_time, std::string& tcstring)
525{
526	U64 hours;
527	U64 minutes;
528	U64 seconds;
529	U64 frames;
530	U64 subframes;
531
532	hours = current_time / (U64)3600000000ul;
533	minutes = current_time / (U64)60000000;
534	minutes %= 60;
535	seconds = current_time / (U64)1000000;
536	seconds %= 60;
537	frames = current_time / (U64)41667;
538	frames %= 24;
539	subframes = current_time / (U64)42;
540	subframes %= 100;
541
542	tcstring = llformat("%3.3d:%2.2d:%2.2d:%2.2d.%2.2d",(int)hours,(int)minutes,(int)seconds,(int)frames,(int)subframes);
543}
544
545
546void secondsToTimecodeString(F32 current_time, std::string& tcstring)
547{
548	microsecondsToTimecodeString((U64)((F64)(SEC_TO_MICROSEC*current_time)), tcstring);
549}
550
551