4 * Contains Windows specific versions of Tcl functions that obtain time
5 * values from the operating system.
7 * Copyright 1995-1998 by Sun Microsystems, Inc.
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 #define SECSPERDAY (60L * 60L * 24L)
16 #define SECSPERYEAR (SECSPERDAY * 365L)
17 #define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY)
20 * Number of samples over which to estimate the performance counter.
26 * The following arrays contain the day of year for the last day of each
27 * month, where index 1 is January.
30 static const int normalDays[] = {
31 -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
34 static const int leapDays[] = {
35 -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
38 typedef struct ThreadSpecificData {
39 char tzName[64]; /* Time zone name */
40 struct tm tm; /* time information */
42 static Tcl_ThreadDataKey dataKey;
45 * Data for managing high-resolution timers.
48 typedef struct TimeInfo {
49 CRITICAL_SECTION cs; /* Mutex guarding this structure. */
50 int initialized; /* Flag == 1 if this structure is
52 int perfCounterAvailable; /* Flag == 1 if the hardware has a performance
54 DWORD calibrationInterv; /* Calibration interval in seconds (start 1 sec) */
55 HANDLE calibrationThread; /* Handle to the thread that keeps the virtual
56 * clock calibrated. */
57 HANDLE readyEvent; /* System event used to trigger the requesting
58 * thread when the clock calibration procedure
59 * is initialized for the first time. */
60 HANDLE exitEvent; /* Event to signal out of an exit handler to
61 * tell the calibration loop to terminate. */
62 LARGE_INTEGER nominalFreq; /* Nominal frequency of the system performance
63 * counter, that is, the value returned from
64 * QueryPerformanceFrequency. */
66 * The following values are used for calculating virtual time. Virtual
67 * time is always equal to:
68 * lastFileTime + (current perf counter - lastCounter)
69 * * 10000000 / curCounterFreq
70 * and lastFileTime and lastCounter are updated any time that virtual time
71 * is returned to a caller.
74 ULARGE_INTEGER fileTimeLastCall;
75 LARGE_INTEGER perfCounterLastCall;
76 LARGE_INTEGER curCounterFreq;
77 LARGE_INTEGER posixEpoch; /* Posix epoch expressed as 100-ns ticks since
78 * the windows epoch. */
81 * Data used in developing the estimate of performance counter frequency
84 Tcl_WideUInt fileTimeSample[SAMPLES];
85 /* Last 64 samples of system time. */
86 Tcl_WideInt perfCounterSample[SAMPLES];
87 /* Last 64 samples of performance counter. */
88 int sampleNo; /* Current sample number. */
91 static TimeInfo timeInfo = {
92 { NULL, 0, 0, NULL, NULL, 0 },
99 #ifdef HAVE_CAST_TO_UNION
100 (LARGE_INTEGER) (Tcl_WideInt) 0,
101 (ULARGE_INTEGER) (DWORDLONG) 0,
102 (LARGE_INTEGER) (Tcl_WideInt) 0,
103 (LARGE_INTEGER) (Tcl_WideInt) 0,
104 (LARGE_INTEGER) (Tcl_WideInt) 0,
118 * Scale to convert wide click values from the TclpGetWideClicks native
119 * resolution to microsecond resolution and back.
122 int initialized; /* 1 if initialized, 0 otherwise */
123 int perfCounter; /* 1 if performance counter usable for wide
125 double microsecsScale; /* Denominator scale between clock / microsecs */
126 } wideClick = {0, 0, 0.0};
130 * Declarations for functions defined later in this file.
133 static struct tm * ComputeGMT(const time_t *tp);
134 static void StopCalibration(ClientData clientData);
135 static DWORD WINAPI CalibrationThread(LPVOID arg);
136 static void UpdateTimeEachSecond(void);
137 static void ResetCounterSamples(Tcl_WideUInt fileTime,
138 Tcl_WideInt perfCounter, Tcl_WideInt perfFreq);
139 static Tcl_WideInt AccumulateSample(Tcl_WideInt perfCounter,
140 Tcl_WideUInt fileTime);
141 static void NativeScaleTime(Tcl_Time* timebuf,
142 ClientData clientData);
143 static Tcl_WideInt NativeGetMicroseconds(void);
144 static void NativeGetTime(Tcl_Time* timebuf,
145 ClientData clientData);
148 * TIP #233 (Virtualized Time): Data for the time hooks, if any.
151 Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime;
152 Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime;
153 ClientData tclTimeClientData = NULL;
156 *----------------------------------------------------------------------
160 * This procedure returns the number of seconds from the epoch. On most
161 * Unix systems the epoch is Midnight Jan 1, 1970 GMT.
164 * Number of seconds from the epoch.
169 *----------------------------------------------------------------------
175 Tcl_WideInt usecSincePosixEpoch;
177 /* Try to use high resolution timer */
178 if ( tclGetTimeProcPtr == NativeGetTime
179 && (usecSincePosixEpoch = NativeGetMicroseconds())
181 return usecSincePosixEpoch / 1000000;
185 tclGetTimeProcPtr(&t, tclTimeClientData); /* Tcl_GetTime inlined. */
191 *----------------------------------------------------------------------
195 * This procedure returns a value that represents the highest resolution
196 * clock available on the system. There are no guarantees on what the
197 * resolution will be. In Tcl we will call this value a "click". The
198 * start time is also system dependent.
201 * Number of clicks from some start time.
206 *----------------------------------------------------------------------
212 Tcl_WideInt usecSincePosixEpoch;
214 /* Try to use high resolution timer */
215 if ( tclGetTimeProcPtr == NativeGetTime
216 && (usecSincePosixEpoch = NativeGetMicroseconds())
218 return (unsigned long)usecSincePosixEpoch;
221 * Use the Tcl_GetTime abstraction to get the time in microseconds, as
222 * nearly as we can, and return it.
225 Tcl_Time now; /* Current Tcl time */
227 tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */
228 return (unsigned long)(now.sec * 1000000) + now.usec;
233 *----------------------------------------------------------------------
235 * TclpGetWideClicks --
237 * This procedure returns a WideInt value that represents the highest
238 * resolution clock in microseconds available on the system.
241 * Number of microseconds (from some start time).
244 * This should be used for time-delta resp. for measurement purposes
245 * only, because on some platforms can return microseconds from some
246 * start time (not from the epoch).
248 *----------------------------------------------------------------------
252 TclpGetWideClicks(void)
254 LARGE_INTEGER curCounter;
256 if (!wideClick.initialized) {
257 LARGE_INTEGER perfCounterFreq;
260 * The frequency of the performance counter is fixed at system boot and
261 * is consistent across all processors. Therefore, the frequency need
262 * only be queried upon application initialization.
264 if (QueryPerformanceFrequency(&perfCounterFreq)) {
265 wideClick.perfCounter = 1;
266 wideClick.microsecsScale = 1000000.0 / perfCounterFreq.QuadPart;
268 /* fallback using microseconds */
269 wideClick.perfCounter = 0;
270 wideClick.microsecsScale = 1;
273 wideClick.initialized = 1;
275 if (wideClick.perfCounter) {
276 if (QueryPerformanceCounter(&curCounter)) {
277 return (Tcl_WideInt)curCounter.QuadPart;
279 /* fallback using microseconds */
280 wideClick.perfCounter = 0;
281 wideClick.microsecsScale = 1;
282 return TclpGetMicroseconds();
284 return TclpGetMicroseconds();
289 *----------------------------------------------------------------------
291 * TclpWideClickInMicrosec --
293 * This procedure return scale to convert wide click values from the
294 * TclpGetWideClicks native resolution to microsecond resolution
298 * 1 click in microseconds as double.
303 *----------------------------------------------------------------------
307 TclpWideClickInMicrosec(void)
309 if (!wideClick.initialized) {
310 (void)TclpGetWideClicks(); /* initialize */
312 return wideClick.microsecsScale;
316 *----------------------------------------------------------------------
318 * TclpGetMicroseconds --
320 * This procedure returns a WideInt value that represents the highest
321 * resolution clock in microseconds available on the system.
324 * Number of microseconds (from the epoch).
329 *----------------------------------------------------------------------
333 TclpGetMicroseconds(void)
335 Tcl_WideInt usecSincePosixEpoch;
337 /* Try to use high resolution timer */
338 if ( tclGetTimeProcPtr == NativeGetTime
339 && (usecSincePosixEpoch = NativeGetMicroseconds())
341 return usecSincePosixEpoch;
344 * Use the Tcl_GetTime abstraction to get the time in microseconds, as
345 * nearly as we can, and return it.
350 tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */
351 return (((Tcl_WideInt)now.sec) * 1000000) + now.usec;
356 *----------------------------------------------------------------------
360 * Gets the current system time in seconds and microseconds since the
361 * beginning of the epoch: 00:00 UCT, January 1, 1970.
364 * Returns the current time in timePtr.
367 * On the first call, initializes a set of static variables to keep track
368 * of the base value of the performance counter, the corresponding wall
369 * clock (obtained through ftime) and the frequency of the performance
370 * counter. Also spins a thread whose function is to wake up periodically
371 * and monitor these values, adjusting them as necessary to correct for
372 * drift in the performance counter's oscillator.
374 *----------------------------------------------------------------------
379 Tcl_Time *timePtr) /* Location to store time information. */
381 Tcl_WideInt usecSincePosixEpoch;
383 /* Try to use high resolution timer */
384 if ( tclGetTimeProcPtr == NativeGetTime
385 && (usecSincePosixEpoch = NativeGetMicroseconds())
387 timePtr->sec = (long) (usecSincePosixEpoch / 1000000);
388 timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000);
390 tclGetTimeProcPtr(timePtr, tclTimeClientData);
395 *----------------------------------------------------------------------
399 * TIP #233: Scale from virtual time to the real-time. For native scaling
400 * the relationship is 1:1 and nothing has to be done.
403 * Scales the time in timePtr.
408 *----------------------------------------------------------------------
414 ClientData clientData)
417 * Native scale is 1:1. Nothing is done.
422 *----------------------------------------------------------------------
424 * NativeGetMicroseconds --
426 * Gets the current system time in microseconds since the beginning
427 * of the epoch: 00:00 UCT, January 1, 1970.
430 * Returns the wide integer with number of microseconds from the epoch, or
431 * 0 if high resolution timer is not available.
434 * On the first call, initializes a set of static variables to keep track
435 * of the base value of the performance counter, the corresponding wall
436 * clock (obtained through ftime) and the frequency of the performance
437 * counter. Also spins a thread whose function is to wake up periodically
438 * and monitor these values, adjusting them as necessary to correct for
439 * drift in the performance counter's oscillator.
441 *----------------------------------------------------------------------
444 static inline Tcl_WideInt
445 NativeCalc100NsTicks(
446 ULONGLONG fileTimeLastCall,
447 LONGLONG perfCounterLastCall,
448 LONGLONG curCounterFreq,
451 return fileTimeLastCall +
452 ((curCounter - perfCounterLastCall) * 10000000 / curCounterFreq);
456 NativeGetMicroseconds(void)
459 * Initialize static storage on the first trip through.
461 * Note: Outer check for 'initialized' is a performance win since it
462 * avoids an extra mutex lock in the common case.
465 if (!timeInfo.initialized) {
467 if (!timeInfo.initialized) {
468 timeInfo.posixEpoch.LowPart = 0xD53E8000;
469 timeInfo.posixEpoch.HighPart = 0x019DB1DE;
471 timeInfo.perfCounterAvailable =
472 QueryPerformanceFrequency(&timeInfo.nominalFreq);
475 * Some hardware abstraction layers use the CPU clock in place of
476 * the real-time clock as a performance counter reference. This
478 * - inconsistent results among the processors on
479 * multi-processor systems.
480 * - unpredictable changes in performance counter frequency on
481 * "gearshift" processors such as Transmeta and SpeedStep.
483 * There seems to be no way to test whether the performance
484 * counter is reliable, but a useful heuristic is that if its
485 * frequency is 1.193182 MHz or 3.579545 MHz, it's derived from a
486 * colorburst crystal and is therefore the RTC rather than the
489 * A sloppier but serviceable heuristic is that the RTC crystal is
490 * normally less than 15 MHz while the TSC crystal is virtually
491 * assured to be greater than 100 MHz. Since Win98SE appears to
492 * fiddle with the definition of the perf counter frequency
493 * (perhaps in an attempt to calibrate the clock?), we use the
494 * latter rule rather than an exact match.
496 * We also assume (perhaps questionably) that the vendors have
497 * gotten their act together on Win64, so bypass all this rubbish
502 if (timeInfo.perfCounterAvailable
504 * The following lines would do an exact match on crystal
506 * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt)1193182
507 * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt)3579545
509 && timeInfo.nominalFreq.QuadPart > (Tcl_WideInt) 15000000){
511 * As an exception, if every logical processor on the system
512 * is on the same chip, we use the performance counter anyway,
513 * presuming that everyone's TSC is locked to the same
517 SYSTEM_INFO systemInfo;
518 unsigned int regs[4];
520 GetSystemInfo(&systemInfo);
521 if (TclWinCPUID(0, regs) == TCL_OK
522 && regs[1] == 0x756E6547 /* "Genu" */
523 && regs[3] == 0x49656E69 /* "ineI" */
524 && regs[2] == 0x6C65746E /* "ntel" */
525 && TclWinCPUID(1, regs) == TCL_OK
526 && ((regs[0]&0x00000F00) == 0x00000F00 /* Pentium 4 */
527 || ((regs[0] & 0x00F00000) /* Extended family */
528 && (regs[3] & 0x10000000))) /* Hyperthread */
529 && (((regs[1]&0x00FF0000) >> 16)/* CPU count */
530 == systemInfo.dwNumberOfProcessors)) {
531 timeInfo.perfCounterAvailable = TRUE;
533 timeInfo.perfCounterAvailable = FALSE;
536 #endif /* above code is Win32 only */
539 * If the performance counter is available, start a thread to
543 if (timeInfo.perfCounterAvailable) {
546 InitializeCriticalSection(&timeInfo.cs);
547 timeInfo.readyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
548 timeInfo.exitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
549 timeInfo.calibrationThread = CreateThread(NULL, 256,
550 CalibrationThread, (LPVOID) NULL, 0, &id);
551 SetThreadPriority(timeInfo.calibrationThread,
552 THREAD_PRIORITY_HIGHEST);
555 * Wait for the thread just launched to start running, and
556 * create an exit handler that kills it so that it doesn't
557 * outlive unloading tclXX.dll
560 WaitForSingleObject(timeInfo.readyEvent, INFINITE);
561 CloseHandle(timeInfo.readyEvent);
562 Tcl_CreateExitHandler(StopCalibration, NULL);
564 timeInfo.initialized = TRUE;
569 if (timeInfo.perfCounterAvailable && timeInfo.curCounterFreq.QuadPart!=0) {
571 * Query the performance counter and use it to calculate the current
575 ULONGLONG fileTimeLastCall;
576 LONGLONG perfCounterLastCall, curCounterFreq;
577 /* Copy with current data of calibration cycle */
579 LARGE_INTEGER curCounter;
580 /* Current performance counter. */
582 QueryPerformanceCounter(&curCounter);
585 * Hold time section locked as short as possible
587 EnterCriticalSection(&timeInfo.cs);
589 fileTimeLastCall = timeInfo.fileTimeLastCall.QuadPart;
590 perfCounterLastCall = timeInfo.perfCounterLastCall.QuadPart;
591 curCounterFreq = timeInfo.curCounterFreq.QuadPart;
593 LeaveCriticalSection(&timeInfo.cs);
596 * If calibration cycle occurred after we get curCounter
599 if (curCounter.QuadPart <= perfCounterLastCall) {
601 * Calibrated file-time is saved from posix in 100-ns ticks
604 return fileTimeLastCall / 10;
608 * If it appears to be more than 1.1 seconds since the last trip
609 * through the calibration loop, the performance counter may have
610 * jumped forward. (See MSDN Knowledge Base article Q274323 for a
611 * description of the hardware problem that makes this test
612 * necessary.) If the counter jumps, we don't want to use it directly.
613 * Instead, we must return system time. Eventually, the calibration
614 * loop should recover.
617 if (curCounter.QuadPart - perfCounterLastCall <
618 11 * curCounterFreq * timeInfo.calibrationInterv / 10
620 /* Calibrated file-time is saved from posix in 100-ns ticks */
621 return NativeCalc100NsTicks(fileTimeLastCall,
622 perfCounterLastCall, curCounterFreq, curCounter.QuadPart) / 10;
627 * High resolution timer is not available.
633 *----------------------------------------------------------------------
637 * TIP #233: Gets the current system time in seconds and microseconds
638 * since the beginning of the epoch: 00:00 UCT, January 1, 1970.
641 * Returns the current time in timePtr.
644 * See NativeGetMicroseconds for more information.
646 *----------------------------------------------------------------------
652 ClientData clientData)
654 Tcl_WideInt usecSincePosixEpoch;
657 * Try to use high resolution timer.
659 if ( (usecSincePosixEpoch = NativeGetMicroseconds()) ) {
660 timePtr->sec = (long) (usecSincePosixEpoch / 1000000);
661 timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000);
664 * High resolution timer is not available. Just use ftime.
670 timePtr->sec = (long)t.time;
671 timePtr->usec = t.millitm * 1000;
676 *----------------------------------------------------------------------
680 * Turns off the calibration thread in preparation for exiting the
687 * Sets the 'exitEvent' event in the 'timeInfo' structure to ask the
688 * thread in question to exit, and waits for it to do so.
690 *----------------------------------------------------------------------
693 void TclWinResetTimerResolution(void);
697 ClientData unused) /* Client data is unused */
699 SetEvent(timeInfo.exitEvent);
702 * If Tcl_Finalize was called from DllMain, the calibration thread is in a
703 * paused state so we need to timeout and continue.
706 WaitForSingleObject(timeInfo.calibrationThread, 100);
707 CloseHandle(timeInfo.exitEvent);
708 CloseHandle(timeInfo.calibrationThread);
712 *----------------------------------------------------------------------
716 * This function converts between seconds and struct tm. If useGMT is
717 * true, then the returned date will be in Greenwich Mean Time (GMT).
718 * Otherwise, it will be in the local time zone.
721 * Returns a static tm structure.
726 *----------------------------------------------------------------------
736 #if defined(_WIN64) || (defined(_USE_64BIT_TIME_T) || (defined(_MSC_VER) && _MSC_VER < 1400))
737 # define t2 *t /* no need to cripple time to 32-bit */
739 time_t t2 = *(__time32_t *)t;
743 #if defined(_MSC_VER) && (_MSC_VER >= 1900)
744 # undef timezone /* prevent conflict with timezone() function */
751 * If we are in the valid range, let the C run-time library handle it.
752 * Otherwise we need to fake it. Note that this algorithm ignores
753 * daylight savings time before the epoch.
757 * Hm, Borland's localtime manages to return NULL under certain
758 * circumstances (e.g. wintime.test, test 1.2). Nobody tests for this,
759 * since 'localtime' isn't supposed to do this, possibly leading to
762 * Patch: We only call this function if we are at least one day into
763 * the epoch, else we handle it ourselves (like we do for times < 0).
764 * H. Giese, June 2003
768 #define LOCALTIME_VALIDITY_BOUNDARY SECSPERDAY
770 #define LOCALTIME_VALIDITY_BOUNDARY 0
773 if (t2 >= LOCALTIME_VALIDITY_BOUNDARY) {
774 return TclpLocaltime(&t2);
777 #if defined(_MSC_VER) && (_MSC_VER >= 1900)
778 _get_timezone(&timezone);
781 time = t2 - timezone;
784 * If we aren't near to overflowing the long, just add the bias and
785 * use the normal calculation. Otherwise we will need to adjust the
789 if (t2 < (LONG_MAX - 2*SECSPERDAY) && t2 > (LONG_MIN + 2*SECSPERDAY)) {
790 tmPtr = ComputeGMT(&time);
792 tmPtr = ComputeGMT(&t2);
797 * Add the bias directly to the tm structure to avoid overflow.
798 * Propagate seconds overflow into minutes, hours and days.
801 time = tmPtr->tm_sec - timezone;
802 tmPtr->tm_sec = (int)(time % 60);
803 if (tmPtr->tm_sec < 0) {
808 time = tmPtr->tm_min + time/60;
809 tmPtr->tm_min = (int)(time % 60);
810 if (tmPtr->tm_min < 0) {
815 time = tmPtr->tm_hour + time/60;
816 tmPtr->tm_hour = (int)(time % 24);
817 if (tmPtr->tm_hour < 0) {
818 tmPtr->tm_hour += 24;
823 tmPtr->tm_mday += (int)time;
824 tmPtr->tm_yday += (int)time;
825 tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7;
828 tmPtr = ComputeGMT(&t2);
834 *----------------------------------------------------------------------
838 * This function computes GMT given the number of seconds since the epoch
839 * (midnight Jan 1 1970).
842 * Returns a (per thread) statically allocated struct tm.
845 * Updates the values of the static struct tm.
847 *----------------------------------------------------------------------
858 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
863 * Compute the 4 year span containing the specified time.
866 tmp = (long)(*tp / SECSPER4YEAR);
867 rem = (long)(*tp % SECSPER4YEAR);
870 * Correct for weird mod semantics so the remainder is always positive.
879 * Compute the year after 1900 by taking the 4 year span and adjusting for
880 * the remainder. This works because 2000 is a leap year, and 1900/2100
881 * are out of the range.
884 tmp = (tmp * 4) + 70;
886 if (rem >= SECSPERYEAR) { /* 1971, etc. */
889 if (rem >= SECSPERYEAR) { /* 1972, etc. */
892 if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */
894 rem -= SECSPERYEAR + SECSPERDAY;
900 tmPtr->tm_year = tmp;
903 * Compute the day of year and leave the seconds in the current day in the
907 tmPtr->tm_yday = rem / SECSPERDAY;
911 * Compute the time of day.
914 tmPtr->tm_hour = rem / 3600;
916 tmPtr->tm_min = rem / 60;
917 tmPtr->tm_sec = rem % 60;
920 * Compute the month and day of month.
923 days = (isLeap) ? leapDays : normalDays;
924 for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) {
927 tmPtr->tm_mon = --tmp;
928 tmPtr->tm_mday = tmPtr->tm_yday - days[tmp];
931 * Compute day of week. Epoch started on a Thursday.
934 tmPtr->tm_wday = (long)(*tp / SECSPERDAY) + 4;
935 if ((*tp % SECSPERDAY) < 0) {
939 if (tmPtr->tm_wday < 0) {
947 *----------------------------------------------------------------------
949 * CalibrationThread --
951 * Thread that manages calibration of the hi-resolution time derived from
952 * the performance counter, to keep it synchronized with the system
956 * arg - Client data from the CreateThread call. This parameter points to
957 * the static TimeInfo structure.
960 * None. This thread embeds an infinite loop.
963 * At an interval of 1s, this thread performs virtual time discipline.
965 * Note: When this thread is entered, TclpInitLock has been called to
966 * safeguard the static storage. There is therefore no synchronization in the
967 * body of this procedure.
969 *----------------------------------------------------------------------
976 FILETIME curFileTime;
980 * Get initial system time and performance counter.
983 GetSystemTimeAsFileTime(&curFileTime);
984 QueryPerformanceCounter(&timeInfo.perfCounterLastCall);
985 QueryPerformanceFrequency(&timeInfo.curCounterFreq);
986 timeInfo.fileTimeLastCall.LowPart = curFileTime.dwLowDateTime;
987 timeInfo.fileTimeLastCall.HighPart = curFileTime.dwHighDateTime;
988 /* Calibrated file-time will be saved from posix in 100-ns ticks */
989 timeInfo.fileTimeLastCall.QuadPart -= timeInfo.posixEpoch.QuadPart;
991 ResetCounterSamples(timeInfo.fileTimeLastCall.QuadPart,
992 timeInfo.perfCounterLastCall.QuadPart,
993 timeInfo.curCounterFreq.QuadPart);
996 * Wake up the calling thread. When it wakes up, it will release the
997 * initialization lock.
1000 SetEvent(timeInfo.readyEvent);
1003 * Run the calibration once a second.
1006 while (timeInfo.perfCounterAvailable) {
1008 * If the exitEvent is set, break out of the loop.
1011 waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE);
1012 if (waitResult == WAIT_OBJECT_0) {
1015 UpdateTimeEachSecond();
1022 *----------------------------------------------------------------------
1024 * UpdateTimeEachSecond --
1026 * Callback from the waitable timer in the clock calibration thread that
1027 * updates system time.
1030 * info - Pointer to the static TimeInfo structure
1036 * Performs virtual time calibration discipline.
1038 *----------------------------------------------------------------------
1042 UpdateTimeEachSecond(void)
1044 LARGE_INTEGER curPerfCounter;
1045 /* Current value returned from
1046 * QueryPerformanceCounter. */
1047 FILETIME curSysTime; /* Current system time. */
1048 static LARGE_INTEGER lastFileTime;
1049 /* File time of the previous calibration */
1050 LARGE_INTEGER curFileTime; /* File time at the time this callback was
1052 Tcl_WideInt estFreq; /* Estimated perf counter frequency. */
1053 Tcl_WideInt vt0; /* Tcl time right now. */
1054 Tcl_WideInt vt1; /* Tcl time one second from now. */
1055 Tcl_WideInt tdiff; /* Difference between system clock and Tcl
1057 Tcl_WideInt driftFreq; /* Frequency needed to drift virtual time into
1058 * step over 1 second. */
1061 * Sample performance counter and system time (from posix epoch).
1064 GetSystemTimeAsFileTime(&curSysTime);
1065 curFileTime.LowPart = curSysTime.dwLowDateTime;
1066 curFileTime.HighPart = curSysTime.dwHighDateTime;
1067 curFileTime.QuadPart -= timeInfo.posixEpoch.QuadPart;
1068 /* If calibration still not needed (check for possible time switch) */
1069 if ( curFileTime.QuadPart > lastFileTime.QuadPart
1070 && curFileTime.QuadPart < lastFileTime.QuadPart +
1071 (timeInfo.calibrationInterv * 10000000)
1073 /* again in next one second */
1076 QueryPerformanceCounter(&curPerfCounter);
1078 lastFileTime.QuadPart = curFileTime.QuadPart;
1081 * We devide by timeInfo.curCounterFreq.QuadPart in several places. That
1082 * value should always be positive on a correctly functioning system. But
1083 * it is good to be defensive about such matters. So if something goes
1084 * wrong and the value does goes to zero, we clear the
1085 * timeInfo.perfCounterAvailable in order to cause the calibration thread
1086 * to shut itself down, then return without additional processing.
1089 if (timeInfo.curCounterFreq.QuadPart == 0){
1090 timeInfo.perfCounterAvailable = 0;
1095 * Several things may have gone wrong here that have to be checked for.
1096 * (1) The performance counter may have jumped.
1097 * (2) The system clock may have been reset.
1099 * In either case, we'll need to reinitialize the circular buffer with
1100 * samples relative to the current system time and the NOMINAL performance
1101 * frequency (not the actual, because the actual has probably run slow in
1102 * the first case). Our estimated frequency will be the nominal frequency.
1104 * Store the current sample into the circular buffer of samples, and
1105 * estimate the performance counter frequency.
1108 estFreq = AccumulateSample(curPerfCounter.QuadPart,
1109 (Tcl_WideUInt) curFileTime.QuadPart);
1112 * We want to adjust things so that time appears to be continuous.
1113 * Virtual file time, right now, is
1115 * vt0 = 10000000 * (curPerfCounter - perfCounterLastCall)
1117 * + fileTimeLastCall
1119 * Ideally, we would like to drift the clock into place over a period of 2
1120 * sec, so that virtual time 2 sec from now will be
1122 * vt1 = 20000000 + curFileTime
1124 * The frequency that we need to use to drift the counter back into place
1125 * is estFreq * 20000000 / (vt1 - vt0)
1128 vt0 = NativeCalc100NsTicks(timeInfo.fileTimeLastCall.QuadPart,
1129 timeInfo.perfCounterLastCall.QuadPart, timeInfo.curCounterFreq.QuadPart,
1130 curPerfCounter.QuadPart);
1132 * If we've gotten more than a second away from system time, then drifting
1133 * the clock is going to be pretty hopeless. Just let it jump. Otherwise,
1134 * compute the drift frequency and fill in everything.
1137 tdiff = vt0 - curFileTime.QuadPart;
1138 if (tdiff > 10000000 || tdiff < -10000000) {
1139 /* jump to current system time, use curent estimated frequency */
1140 vt0 = curFileTime.QuadPart;
1142 /* calculate new frequency and estimate drift to the next second */
1143 vt1 = 20000000 + curFileTime.QuadPart;
1144 driftFreq = (estFreq * 20000000 / (vt1 - vt0));
1146 * Avoid too large drifts (only half of the current difference),
1147 * that allows also be more accurate (aspire to the smallest tdiff),
1148 * so then we can prolong calibration interval by tdiff < 100000
1150 driftFreq = timeInfo.curCounterFreq.QuadPart +
1151 (driftFreq - timeInfo.curCounterFreq.QuadPart) / 2;
1154 * Average between estimated, 2 current and 5 drifted frequencies,
1155 * (do the soft drifting as possible)
1157 estFreq = (estFreq + 2 * timeInfo.curCounterFreq.QuadPart + 5 * driftFreq) / 8;
1160 /* Avoid too large discrepancy from nominal frequency */
1161 if (estFreq > 1003*timeInfo.nominalFreq.QuadPart/1000) {
1162 estFreq = 1003*timeInfo.nominalFreq.QuadPart/1000;
1163 vt0 = curFileTime.QuadPart;
1164 } else if (estFreq < 997*timeInfo.nominalFreq.QuadPart/1000) {
1165 estFreq = 997*timeInfo.nominalFreq.QuadPart/1000;
1166 vt0 = curFileTime.QuadPart;
1167 } else if (vt0 != curFileTime.QuadPart) {
1169 * Be sure the clock ticks never backwards (avoid it by negative drifting)
1170 * just compare native time (in 100-ns) before and hereafter using
1171 * new calibrated values) and do a small adjustment (short time freeze)
1173 LARGE_INTEGER newPerfCounter;
1174 Tcl_WideInt nt0, nt1;
1176 QueryPerformanceCounter(&newPerfCounter);
1177 nt0 = NativeCalc100NsTicks(timeInfo.fileTimeLastCall.QuadPart,
1178 timeInfo.perfCounterLastCall.QuadPart, timeInfo.curCounterFreq.QuadPart,
1179 newPerfCounter.QuadPart);
1180 nt1 = NativeCalc100NsTicks(vt0,
1181 curPerfCounter.QuadPart, estFreq,
1182 newPerfCounter.QuadPart);
1183 if (nt0 > nt1) { /* drifted backwards, try to compensate with new base */
1184 /* first adjust with a micro jump (short frozen time is acceptable) */
1186 /* if drift unavoidable (e. g. we had a time switch), then reset it */
1187 vt1 = vt0 - curFileTime.QuadPart;
1188 if (vt1 > 10000000 || vt1 < -10000000) {
1189 /* larger jump resp. shift relative new file-time */
1190 vt0 = curFileTime.QuadPart;
1195 /* In lock commit new values to timeInfo (hold lock as short as possible) */
1196 EnterCriticalSection(&timeInfo.cs);
1198 /* grow calibration interval up to 10 seconds (if still precise enough) */
1199 if (tdiff < -100000 || tdiff > 100000) {
1200 /* too long drift - reset calibration interval to 1000 second */
1201 timeInfo.calibrationInterv = 1;
1202 } else if (timeInfo.calibrationInterv < 10) {
1203 timeInfo.calibrationInterv++;
1206 timeInfo.fileTimeLastCall.QuadPart = vt0;
1207 timeInfo.curCounterFreq.QuadPart = estFreq;
1208 timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart;
1210 LeaveCriticalSection(&timeInfo.cs);
1214 *----------------------------------------------------------------------
1216 * ResetCounterSamples --
1218 * Fills the sample arrays in 'timeInfo' with dummy values that will
1219 * yield the current performance counter and frequency.
1225 * The array of samples is filled in so that it appears that there are
1226 * SAMPLES samples at one-second intervals, separated by precisely the
1229 *----------------------------------------------------------------------
1233 ResetCounterSamples(
1234 Tcl_WideUInt fileTime, /* Current file time */
1235 Tcl_WideInt perfCounter, /* Current performance counter */
1236 Tcl_WideInt perfFreq) /* Target performance frequency */
1239 for (i=SAMPLES-1 ; i>=0 ; --i) {
1240 timeInfo.perfCounterSample[i] = perfCounter;
1241 timeInfo.fileTimeSample[i] = fileTime;
1242 perfCounter -= perfFreq;
1243 fileTime -= 10000000;
1245 timeInfo.sampleNo = 0;
1249 *----------------------------------------------------------------------
1251 * AccumulateSample --
1253 * Updates the circular buffer of performance counter and system time
1254 * samples with a new data point.
1260 * The new data point replaces the oldest point in the circular buffer,
1261 * and the descriptive statistics are updated to accumulate the new
1264 * Several things may have gone wrong here that have to be checked for.
1265 * (1) The performance counter may have jumped.
1266 * (2) The system clock may have been reset.
1268 * In either case, we'll need to reinitialize the circular buffer with samples
1269 * relative to the current system time and the NOMINAL performance frequency
1270 * (not the actual, because the actual has probably run slow in the first
1276 Tcl_WideInt perfCounter,
1277 Tcl_WideUInt fileTime)
1279 Tcl_WideUInt workFTSample; /* File time sample being removed from or
1280 * added to the circular buffer. */
1281 Tcl_WideInt workPCSample; /* Performance counter sample being removed
1282 * from or added to the circular buffer. */
1283 Tcl_WideUInt lastFTSample; /* Last file time sample recorded */
1284 Tcl_WideInt lastPCSample; /* Last performance counter sample recorded */
1285 Tcl_WideInt FTdiff; /* Difference between last FT and current */
1286 Tcl_WideInt PCdiff; /* Difference between last PC and current */
1287 Tcl_WideInt estFreq; /* Estimated performance counter frequency */
1290 * Test for jumps and reset the samples if we have one.
1293 if (timeInfo.sampleNo == 0) {
1295 timeInfo.perfCounterSample[timeInfo.sampleNo + SAMPLES - 1];
1297 timeInfo.fileTimeSample[timeInfo.sampleNo + SAMPLES - 1];
1299 lastPCSample = timeInfo.perfCounterSample[timeInfo.sampleNo - 1];
1300 lastFTSample = timeInfo.fileTimeSample[timeInfo.sampleNo - 1];
1303 PCdiff = perfCounter - lastPCSample;
1304 FTdiff = fileTime - lastFTSample;
1305 if (PCdiff < timeInfo.nominalFreq.QuadPart * 9 / 10
1306 || PCdiff > timeInfo.nominalFreq.QuadPart * 11 / 10
1307 || FTdiff < 9000000 || FTdiff > 11000000) {
1308 ResetCounterSamples(fileTime, perfCounter,
1309 timeInfo.nominalFreq.QuadPart);
1310 return timeInfo.nominalFreq.QuadPart;
1313 * Estimate the frequency.
1316 workPCSample = timeInfo.perfCounterSample[timeInfo.sampleNo];
1317 workFTSample = timeInfo.fileTimeSample[timeInfo.sampleNo];
1318 estFreq = 10000000 * (perfCounter - workPCSample)
1319 / (fileTime - workFTSample);
1320 timeInfo.perfCounterSample[timeInfo.sampleNo] = perfCounter;
1321 timeInfo.fileTimeSample[timeInfo.sampleNo] = (Tcl_WideInt) fileTime;
1324 * Advance the sample number.
1327 if (++timeInfo.sampleNo >= SAMPLES) {
1328 timeInfo.sampleNo = 0;
1336 *----------------------------------------------------------------------
1340 * Wrapper around the 'gmtime' library function to make it thread safe.
1343 * Returns a pointer to a 'struct tm' in thread-specific data.
1346 * Invokes gmtime or gmtime_r as appropriate.
1348 *----------------------------------------------------------------------
1353 const time_t *timePtr) /* Pointer to the number of seconds since the
1354 * local system's epoch */
1357 * The MS implementation of gmtime is thread safe because it returns the
1358 * time in a block of thread-local storage, and Windows does not provide a
1359 * Posix gmtime_r function.
1362 #if defined(_WIN64) || defined(_USE_64BIT_TIME_T) || (defined(_MSC_VER) && _MSC_VER < 1400)
1363 return gmtime(timePtr);
1365 return _gmtime32((const __time32_t *)timePtr);
1370 *----------------------------------------------------------------------
1374 * Wrapper around the 'localtime' library function to make it thread
1378 * Returns a pointer to a 'struct tm' in thread-specific data.
1381 * Invokes localtime or localtime_r as appropriate.
1383 *----------------------------------------------------------------------
1388 const time_t *timePtr) /* Pointer to the number of seconds since the
1389 * local system's epoch */
1392 * The MS implementation of localtime is thread safe because it returns
1393 * the time in a block of thread-local storage, and Windows does not
1394 * provide a Posix localtime_r function.
1397 #if defined(_WIN64) || defined(_USE_64BIT_TIME_T) || (defined(_MSC_VER) && _MSC_VER < 1400)
1398 return localtime(timePtr);
1400 return _localtime32((const __time32_t *)timePtr);
1405 *----------------------------------------------------------------------
1407 * Tcl_SetTimeProc --
1409 * TIP #233 (Virtualized Time): Registers two handlers for the
1410 * virtualization of Tcl's access to time information.
1416 * Remembers the handlers, alters core behaviour.
1418 *----------------------------------------------------------------------
1423 Tcl_GetTimeProc *getProc,
1424 Tcl_ScaleTimeProc *scaleProc,
1425 ClientData clientData)
1427 tclGetTimeProcPtr = getProc;
1428 tclScaleTimeProcPtr = scaleProc;
1429 tclTimeClientData = clientData;
1433 *----------------------------------------------------------------------
1435 * Tcl_QueryTimeProc --
1437 * TIP #233 (Virtualized Time): Query which time handlers are registered.
1445 *----------------------------------------------------------------------
1450 Tcl_GetTimeProc **getProc,
1451 Tcl_ScaleTimeProc **scaleProc,
1452 ClientData *clientData)
1455 *getProc = tclGetTimeProcPtr;
1458 *scaleProc = tclScaleTimeProcPtr;
1461 *clientData = tclTimeClientData;