1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012-2015 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/misc/time_nl.h"
23 #include "nel/misc/sstring.h"
24 #include "nel/misc/thread.h"
27 # include <MMSystem.h>
28 #elif defined (NL_OS_UNIX)
29 # include <sys/time.h>
34 #include <mach/mach.h>
35 #include <mach/mach_time.h>
47 bool a_HaveQueryPerformance
= false;
48 LARGE_INTEGER a_QueryPerformanceFrequency
;
51 # if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
52 # if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK >= 0)
53 # define NL_MONOTONIC_CLOCK
56 # ifdef NL_MONOTONIC_CLOCK
57 bool a_CheckedMonotonicClock
= false;
58 bool a_HasMonotonicClock
= false;
59 uint64 a_MonotonicClockFrequency
= 0;
60 uint64 a_MonotonicClockResolutionNs
= 0;
61 bool hasMonotonicClock()
63 if (!a_CheckedMonotonicClock
)
65 /* Initialize the local time engine.
66 * On Unix, this method will find out if the Monotonic Clock is supported
67 * (seems supported by kernel 2.6, not by kernel 2.4). See getLocalTime().
70 if ((clock_gettime( CLOCK_MONOTONIC
, &tv
) == 0) &&
71 (clock_getres( CLOCK_MONOTONIC
, &tv
) == 0))
73 // nldebug( "Monotonic local time supported (resolution %.6f ms)", ((float)tv.tv_sec)*1000.0f + ((float)tv.tv_nsec)/1000000.0f );
77 nlwarning("Monotonic clock not ok, resolution > 1s");
78 a_HasMonotonicClock
= false;
82 uint64 nsPerTick
= tv
.tv_nsec
;
83 uint64 nsPerSec
= 1000000000L;
84 uint64 tickPerSec
= nsPerSec
/ nsPerTick
;
85 a_MonotonicClockFrequency
= tickPerSec
;
86 a_MonotonicClockResolutionNs
= nsPerTick
;
87 a_HasMonotonicClock
= true;
92 a_HasMonotonicClock
= false;
94 a_CheckedMonotonicClock
= true;
96 return a_HasMonotonicClock
;
102 void CTime::probeTimerInfo(CTime::CTimerInfo
&result
)
107 LARGE_INTEGER winPerfFreq
;
108 LARGE_INTEGER winPerfCount
;
110 if (!QueryPerformanceFrequency(&winPerfFreq
))
112 nldebug("Cannot query performance frequency");
113 result
.IsHighPrecisionAvailable
= false;
117 result
.HighPrecisionResolution
= winPerfFreq
.QuadPart
;
119 if (winPerfFreq
.QuadPart
== 1000)
121 nldebug("Higher precision timer not available, OS defaulted to GetTickCount");
122 result
.IsHighPrecisionAvailable
= false;
124 if (!QueryPerformanceCounter(&winPerfCount
))
126 nldebug("Cannot query performance counter");
127 result
.IsHighPrecisionAvailable
= false;
128 result
.HighPrecisionResolution
= 1000;
130 a_HaveQueryPerformance
= result
.IsHighPrecisionAvailable
;
131 a_QueryPerformanceFrequency
.QuadPart
= winPerfFreq
.QuadPart
;
132 if (!result
.IsHighPrecisionAvailable
)
134 lowResTime
= timeGetTime();
138 // Other platforms are awesome. Generic implementation for now.
139 TTime localTime
= getLocalTime();
140 result
.IsHighPrecisionAvailable
= true;
141 result
.HighPrecisionResolution
= 0;
143 # ifdef NL_MONOTONIC_CLOCK
145 if (hasMonotonicClock())
147 clock_gettime(CLOCK_MONOTONIC
, &monoClock
);
148 result
.HighPrecisionResolution
= a_MonotonicClockFrequency
;
152 nldebug("Monotonic clock not available");
158 uint64 cpuMask
= IProcess::getCurrentProcess()->getCPUMask();
160 uint64 threadMask
= IThread::getCurrentThread()->getCPUMask(); // broken on linux, don't expect it to work anywhere
162 uint64 threadMask
= cpuMask
;
165 uint identical
= 0; // Identical stamps may indicate the os handling backwards glitches.
166 uint backwards
= 0; // Happens when the timers are not always in sync and the implementation is faulty.
167 uint regular
= 0; // How many times the number advanced normally.
168 uint skipping
= 0; // Does not really mean anything necessarily.
169 uint frequencybug
= 0; // Should never happen.
170 // uint badcore = 0; // Affinity does not work.
172 // Cycle 32 times trough all cores, and verify if the timing remains consistent.
173 for (uint i
= 32; i
; --i
)
175 uint64 currentBit
= 1;
176 for (uint j
= 64; j
; --j
)
178 if (cpuMask
& currentBit
)
181 if (!IThread::getCurrentThread()->setCPUMask(currentBit
))
183 if (!IProcess::getCurrentProcess()->setCPUMask(currentBit
))
185 break; // Thread was set to last cpu.
187 // Make sure the thread is rescheduled.
191 /* Can only verify on 2003, Vista and higher.
192 if (1 << GetCurrentProcessorNumber() != currentBit)
195 // Check if the timer is still sane.
196 if (result
.IsHighPrecisionAvailable
)
198 LARGE_INTEGER winPerfFreqN
;
199 LARGE_INTEGER winPerfCountN
;
200 QueryPerformanceFrequency(&winPerfFreqN
);
201 if (winPerfFreqN
.QuadPart
!= winPerfFreq
.QuadPart
)
203 QueryPerformanceCounter(&winPerfCountN
);
204 if (winPerfCountN
.QuadPart
== winPerfCount
.QuadPart
)
206 if (winPerfCountN
.QuadPart
< winPerfCount
.QuadPart
|| winPerfCountN
.QuadPart
- winPerfCount
.QuadPart
< 0)
208 if (winPerfCountN
.QuadPart
- winPerfCount
.QuadPart
> winPerfFreq
.QuadPart
/ 20) // 50ms skipping check
210 else if (winPerfCountN
.QuadPart
> winPerfCount
.QuadPart
)
212 winPerfCount
.QuadPart
= winPerfCountN
.QuadPart
;
217 lowResTimeN
= timeGetTime();
218 if (lowResTimeN
== lowResTime
)
220 if (lowResTimeN
< lowResTime
|| lowResTimeN
- lowResTime
< 0)
222 if (lowResTimeN
- lowResTime
> 50)
224 else if (lowResTimeN
> lowResTime
)
226 lowResTime
= lowResTimeN
;
234 # ifdef NL_MONOTONIC_CLOCK
235 if (hasMonotonicClock())
238 clock_gettime(CLOCK_MONOTONIC
, &monoClockN
);
239 if (monoClock
.tv_sec
== monoClockN
.tv_sec
&& monoClock
.tv_nsec
== monoClockN
.tv_nsec
)
241 if (monoClockN
.tv_sec
< monoClock
.tv_sec
|| (monoClock
.tv_sec
== monoClockN
.tv_sec
&& monoClockN
.tv_nsec
< monoClock
.tv_nsec
))
243 if (monoClock
.tv_sec
== monoClockN
.tv_sec
&& (monoClockN
.tv_nsec
- monoClock
.tv_nsec
> 50000000L))
245 else if ((monoClock
.tv_sec
== monoClockN
.tv_sec
&& monoClock
.tv_nsec
< monoClockN
.tv_nsec
) || monoClock
.tv_sec
< monoClockN
.tv_sec
)
247 monoClock
.tv_sec
= monoClockN
.tv_sec
;
248 monoClock
.tv_nsec
= monoClockN
.tv_nsec
;
253 TTime localTimeN
= getLocalTime();
254 if (localTimeN
== localTime
)
256 if (localTimeN
< localTime
|| localTimeN
- localTime
< 0)
258 if (localTimeN
- localTime
> 50)
260 else if (localTimeN
> localTime
)
262 localTime
= localTimeN
;
271 IThread::getCurrentThread()->setCPUMask(threadMask
);
273 IProcess::getCurrentProcess()->setCPUMask(threadMask
);
276 nldebug("Timer resolution: %i Hz", (int)(result
.HighPrecisionResolution
));
277 nldebug("Time identical: %i, backwards: %i, regular: %i, skipping: %i, frequency bug: %i", identical
, backwards
, regular
, skipping
, frequencybug
);
278 if (identical
> regular
)
279 nlwarning("The system timer is of relatively low resolution, you may experience issues");
280 if (backwards
> 0 || frequencybug
> 0)
282 nlwarning("The current system timer is not reliable across multiple cpu cores");
283 result
.RequiresSingleCore
= true;
285 else result
.RequiresSingleCore
= false;
287 if (result
.HighPrecisionResolution
== 14318180)
289 nldebug("Detected known HPET era timer frequency");
291 if (result
.HighPrecisionResolution
== 3579545)
293 nldebug("Detected known AHCI era timer frequency");
295 if (result
.HighPrecisionResolution
== 1193182)
297 nldebug("Detected known i8253/i8254 era timer frequency");
302 /* Return the number of second since midnight (00:00:00), January 1, 1970,
303 * coordinated universal time, according to the system clock.
304 * This values is the same on all computer if computers are synchronized (with NTP for example).
306 uint32
CTime::getSecondsSince1970 ()
308 return uint32(time(NULL
));
311 /** Return the number of second since midnight (00:00:00), January 1, 1970,
312 * coordinated universal time, according to the system clock.
313 * The time returned is UTC (aka GMT+0), ie it does not have the local time ajustement
314 * nor it have the daylight saving ajustement.
315 * This values is the same on all computer if computers are synchronized (with NTP for example).
317 //uint32 CTime::getSecondsSince1970UTC ()
319 // // get the local time
320 // time_t nowLocal = time(NULL);
321 // // convert it to GMT time (UTC)
322 // struct tm * timeinfo;
323 // timeinfo = gmtime(&nowLocal);
324 // return nl_mktime(timeinfo);
327 /* Return the local time in milliseconds.
328 * Use it only to measure time difference, the absolute value does not mean anything.
329 * On Unix, getLocalTime() will try to use the Monotonic Clock if available, otherwise
330 * the value can jump backwards if the system time is changed by a user or a NTP time sync process.
331 * The value is different on 2 different computers; use the CUniTime class to get a universal
332 * time that is the same on all computers.
333 * \warning On Win32, the value is on 32 bits only. It wraps around to 0 every about 49.71 days.
335 TTime
CTime::getLocalTime ()
340 //static bool initdone = false;
341 //static bool byperfcounter;
345 //byperfcounter = (getPerformanceTime() != 0);
349 /* Retrieve time is ms
350 * Why do we prefer getPerformanceTime() to timeGetTime() ? Because on one dual-processor Win2k
351 * PC, we have noticed that timeGetTime() slows down when the client is running !!!
353 /* Now we have noticed that on all WinNT4 PC the getPerformanceTime can give us value that
354 * are less than previous
357 //if ( byperfcounter )
359 // return (TTime)(ticksToSecond(getPerformanceTime()) * 1000.0f);
363 // This is not affected by system time changes. But it cycles every 49 days.
364 // return timeGetTime(); // Only this was left active before it was commented.
368 * The above is no longer relevant.
371 if (a_HaveQueryPerformance
)
373 // On a (fast) 15MHz timer this rolls over after 7000 days.
374 // If my calculations are right.
375 LARGE_INTEGER counter
;
376 QueryPerformanceCounter(&counter
);
377 counter
.QuadPart
*= (LONGLONG
)1000L;
378 counter
.QuadPart
/= a_QueryPerformanceFrequency
.QuadPart
;
379 return counter
.QuadPart
;
383 // Use default reliable low resolution timer.
384 return timeGetTime();
387 #elif defined (NL_OS_UNIX)
389 #ifdef NL_MONOTONIC_CLOCK
391 if (hasMonotonicClock())
394 // This is not affected by system time changes.
395 if ( clock_gettime( CLOCK_MONOTONIC
, &tv
) != 0 )
396 nlerror ("Can't get clock time again");
397 return (TTime
)tv
.tv_sec
* (TTime
)1000 + (TTime
)((tv
.tv_nsec
/*+500*/) / 1000000);
402 // This is affected by system time changes.
404 if ( gettimeofday( &tv
, NULL
) != 0 )
405 nlerror ("Can't get time of day");
406 return (TTime
)tv
.tv_sec
* (TTime
)1000 + (TTime
)tv
.tv_usec
/ (TTime
)1000;
411 /* Return the time in processor ticks. Use it for profile purpose.
412 * If the performance time is not supported on this hardware, it returns 0.
413 * \warning On a multiprocessor system, the value returned by each processor may
414 * be different. The only way to workaround this is to set a processor affinity
415 * to the measured thread.
416 * \warning The speed of tick increase can vary (especially on laptops or CPUs with
417 * power management), so profiling several times and computing the average could be
420 TTicks
CTime::getPerformanceTime ()
424 if (QueryPerformanceCounter (&ret
))
428 #elif defined(NL_OS_MAC)
429 return mach_absolute_time();
431 #if defined(HAVE_X86_64)
433 __asm__
volatile (".byte 0x0f, 0x31" : "=a" (lo
), "=d" (hi
));
434 return (hi
<< 32) | (lo
& 0xffffffff);
435 #elif defined(HAVE_X86) and !defined(NL_OS_MAC)
437 // RDTSC - Read time-stamp counter into EDX:EAX.
438 __asm__
volatile (".byte 0x0f, 0x31" : "=A" (x
));
441 static bool firstWarn
= true;
444 nlwarning ("TTicks CTime::getPerformanceTime () is not implemented for your processor, returning 0");
450 #endif // NL_OS_WINDOWS
453 #define GETTICKS(t) asm volatile ("push %%esi\n\t" "mov %0, %%esi" : : "r" (t)); \
454 asm volatile ("push %eax\n\t" "push %edx"); \
455 asm volatile ("rdtsc"); \
456 asm volatile ("movl %eax, (%esi)\n\t" "movl %edx, 4(%esi)"); \
457 asm volatile ("pop %edx\n\t" "pop %eax\n\t" "pop %esi");
461 /* Convert a ticks count into second. If the performance time is not supported on this
462 * hardware, it returns 0.0.
464 double CTime::ticksToSecond (TTicks ticks
)
468 if (QueryPerformanceFrequency(&ret
))
470 return (double)(sint64
)ticks
/(double)ret
.QuadPart
;
473 #elif defined(NL_OS_MAC)
475 static double factor
= 0.0;
478 mach_timebase_info_data_t tbInfo
;
479 mach_timebase_info(&tbInfo
);
480 factor
= 1000000000.0 * (double)tbInfo
.numer
/ (double)tbInfo
.denom
;
482 return double(ticks
/ factor
);
484 #endif // NL_OS_WINDOWS
486 static bool benchFrequency
= true;
487 static sint64 freq
= 0;
490 // try to have an estimation of the cpu frequency
492 TTicks tickBefore
= getPerformanceTime ();
493 TTicks tickAfter
= tickBefore
;
494 TTime timeBefore
= getLocalTime ();
495 TTime timeAfter
= timeBefore
;
498 if (timeAfter
- timeBefore
> 1000)
500 timeAfter
= getLocalTime ();
501 tickAfter
= getPerformanceTime ();
504 TTime timeDelta
= timeAfter
- timeBefore
;
505 TTicks tickDelta
= tickAfter
- tickBefore
;
507 freq
= 1000 * tickDelta
/ timeDelta
;
508 benchFrequency
= false;
511 return (double)(sint64
)ticks
/(double)freq
;
516 std::string
CTime::getHumanRelativeTime(sint32 nbSeconds
)
518 sint32 delta
= nbSeconds
;
522 // some constants of time duration in seconds
523 const sint32 oneMinute
= 60;
524 const sint32 oneHour
= oneMinute
* 60;
525 const sint32 oneDay
= oneHour
* 24;
526 const sint32 oneWeek
= oneDay
* 7;
527 const sint32 oneMonth
= oneDay
* 30; // aprox, a more precise value is 30.416666... but no matter
528 const sint32 oneYear
= oneDay
* 365; // aprox, a more precise value is 365.26.. who care?
530 sint32 year
, month
, week
, day
, hour
, minute
;
531 year
= month
= week
= day
= hour
= minute
= 0;
533 /// compute the different parts
534 year
= delta
/ oneYear
;
537 month
= delta
/ oneMonth
;
540 week
= delta
/ oneWeek
;
543 day
= delta
/ oneDay
;
546 hour
= delta
/ oneHour
;
549 minute
= delta
/ oneMinute
;
552 // compute the string
556 ret
<< year
<< " years ";
558 ret
<< month
<< " months ";
560 ret
<< week
<< " weeks ";
562 ret
<< day
<< " days ";
564 ret
<< hour
<< " hours ";
566 ret
<< minute
<< " minutes ";
567 if (delta
|| ret
.empty())
568 ret
<< delta
<< " seconds ";
574 /** Return the offset in 10th of micro sec between the windows base time (
575 * 01-01-1601 0:0:0 UTC) and the unix base time (01-01-1970 0:0:0 UTC).
576 * This value is used to convert windows system and file time back and
577 * forth to unix time (aka epoch)
579 uint64
CTime::getWindowsToUnixBaseTimeOffset()
581 static bool init
= false;
583 static uint64 offset
= 0;
587 // compute the offset to convert windows base time into unix time (aka epoch)
588 // build a WIN32 system time for jan 1, 1970
590 baseTime
.wYear
= 1970;
592 baseTime
.wDayOfWeek
= 0;
595 baseTime
.wMinute
= 0;
596 baseTime
.wSecond
= 0;
597 baseTime
.wMilliseconds
= 0;
599 FILETIME baseFileTime
= {0,0};
600 // convert it into a FILETIME value
601 SystemTimeToFileTime(&baseTime
, &baseFileTime
);
602 offset
= baseFileTime
.dwLowDateTime
| (uint64(baseFileTime
.dwHighDateTime
)<<32);