1/*
2 * tclWinTime.c --
3 *
4 *	Contains Windows specific versions of Tcl functions that
5 *	obtain time values from the operating system.
6 *
7 * Copyright 1995-1998 by Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tclWinTime.c,v 1.14.2.11 2007/04/21 19:52:15 kennykb Exp $
13 */
14
15#include "tclWinInt.h"
16
17#define SECSPERDAY (60L * 60L * 24L)
18#define SECSPERYEAR (SECSPERDAY * 365L)
19#define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY)
20
21/*
22 * Number of samples over which to estimate the performance counter
23 */
24#define SAMPLES 64
25
26/*
27 * The following arrays contain the day of year for the last day of
28 * each month, where index 1 is January.
29 */
30
31static int normalDays[] = {
32    -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
33};
34
35static int leapDays[] = {
36    -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
37};
38
39typedef struct ThreadSpecificData {
40    char tzName[64];		/* Time zone name */
41    struct tm tm;		/* time information */
42} ThreadSpecificData;
43static Tcl_ThreadDataKey dataKey;
44
45/*
46 * Data for managing high-resolution timers.
47 */
48
49typedef struct TimeInfo {
50
51    CRITICAL_SECTION cs;	/* Mutex guarding this structure */
52
53    int initialized;		/* Flag == 1 if this structure is
54				 * initialized. */
55
56    int perfCounterAvailable;	/* Flag == 1 if the hardware has a
57				 * performance counter */
58
59    HANDLE calibrationThread;	/* Handle to the thread that keeps the
60				 * virtual clock calibrated. */
61
62    HANDLE readyEvent;		/* System event used to
63				 * trigger the requesting thread
64				 * when the clock calibration procedure
65				 * is initialized for the first time */
66
67    HANDLE exitEvent; 		/* Event to signal out of an exit handler
68				 * to tell the calibration loop to
69				 * terminate */
70
71    LARGE_INTEGER nominalFreq;	/* Nominal frequency of the system
72				 * performance counter, that is, the value
73				 * returned from QueryPerformanceFrequency. */
74
75    /*
76     * The following values are used for calculating virtual time.
77     * Virtual time is always equal to:
78     *    lastFileTime + (current perf counter - lastCounter)
79     *				* 10000000 / curCounterFreq
80     * and lastFileTime and lastCounter are updated any time that
81     * virtual time is returned to a caller.
82     */
83
84    ULARGE_INTEGER fileTimeLastCall;
85    LARGE_INTEGER perfCounterLastCall;
86    LARGE_INTEGER curCounterFreq;
87
88    /*
89     * Data used in developing the estimate of performance counter
90     * frequency
91     */
92    Tcl_WideUInt fileTimeSample[SAMPLES];
93				/* Last 64 samples of system time */
94    Tcl_WideInt perfCounterSample[SAMPLES];
95				/* Last 64 samples of performance counter */
96    int sampleNo;		/* Current sample number */
97
98
99} TimeInfo;
100
101static TimeInfo timeInfo = {
102    { NULL },
103    0,
104    0,
105    (HANDLE) NULL,
106    (HANDLE) NULL,
107    (HANDLE) NULL,
108#ifdef HAVE_CAST_TO_UNION
109    (LARGE_INTEGER) (Tcl_WideInt) 0,
110    (ULARGE_INTEGER) (DWORDLONG) 0,
111    (LARGE_INTEGER) (Tcl_WideInt) 0,
112    (LARGE_INTEGER) (Tcl_WideInt) 0,
113#else
114    0,
115    0,
116    0,
117    0,
118#endif
119    { 0 },
120    { 0 },
121    0
122};
123
124CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
125
126/*
127 * Declarations for functions defined later in this file.
128 */
129
130static struct tm *	ComputeGMT _ANSI_ARGS_((const time_t *tp));
131static void		StopCalibration _ANSI_ARGS_(( ClientData ));
132static DWORD WINAPI     CalibrationThread _ANSI_ARGS_(( LPVOID arg ));
133static void 		UpdateTimeEachSecond _ANSI_ARGS_(( void ));
134static void		ResetCounterSamples _ANSI_ARGS_((
135			    Tcl_WideUInt fileTime,
136                            Tcl_WideInt perfCounter,
137			    Tcl_WideInt perfFreq
138			));
139static Tcl_WideInt		AccumulateSample _ANSI_ARGS_((
140			    Tcl_WideInt perfCounter,
141			    Tcl_WideUInt fileTime
142			));
143
144/*
145 *----------------------------------------------------------------------
146 *
147 * TclpGetSeconds --
148 *
149 *	This procedure returns the number of seconds from the epoch.
150 *	On most Unix systems the epoch is Midnight Jan 1, 1970 GMT.
151 *
152 * Results:
153 *	Number of seconds from the epoch.
154 *
155 * Side effects:
156 *	None.
157 *
158 *----------------------------------------------------------------------
159 */
160
161unsigned long
162TclpGetSeconds()
163{
164    Tcl_Time t;
165    Tcl_GetTime( &t );
166    return t.sec;
167}
168
169/*
170 *----------------------------------------------------------------------
171 *
172 * TclpGetClicks --
173 *
174 *	This procedure returns a value that represents the highest
175 *	resolution clock available on the system.  There are no
176 *	guarantees on what the resolution will be.  In Tcl we will
177 *	call this value a "click".  The start time is also system
178 *	dependant.
179 *
180 * Results:
181 *	Number of clicks from some start time.
182 *
183 * Side effects:
184 *	None.
185 *
186 *----------------------------------------------------------------------
187 */
188
189unsigned long
190TclpGetClicks()
191{
192    /*
193     * Use the Tcl_GetTime abstraction to get the time in microseconds,
194     * as nearly as we can, and return it.
195     */
196
197    Tcl_Time now;		/* Current Tcl time */
198    unsigned long retval;	/* Value to return */
199
200    Tcl_GetTime( &now );
201    retval = ( now.sec * 1000000 ) + now.usec;
202    return retval;
203
204}
205
206/*
207 *----------------------------------------------------------------------
208 *
209 * TclpGetTimeZone --
210 *
211 *	Determines the current timezone.  The method varies wildly
212 *	between different Platform implementations, so its hidden in
213 *	this function.
214 *
215 * Results:
216 *	Minutes west of GMT.
217 *
218 * Side effects:
219 *	None.
220 *
221 *----------------------------------------------------------------------
222 */
223
224int
225TclpGetTimeZone (currentTime)
226    Tcl_WideInt currentTime;
227{
228    int timeZone;
229
230    tzset();
231    timeZone = _timezone / 60;
232
233    return timeZone;
234}
235
236/*
237 *----------------------------------------------------------------------
238 *
239 * Tcl_GetTime --
240 *
241 *	Gets the current system time in seconds and microseconds
242 *	since the beginning of the epoch: 00:00 UCT, January 1, 1970.
243 *
244 * Results:
245 *	Returns the current time in timePtr.
246 *
247 * Side effects:
248 *	On the first call, initializes a set of static variables to
249 *	keep track of the base value of the performance counter, the
250 *	corresponding wall clock (obtained through ftime) and the
251 *	frequency of the performance counter.  Also spins a thread
252 *	whose function is to wake up periodically and monitor these
253 *	values, adjusting them as necessary to correct for drift
254 *	in the performance counter's oscillator.
255 *
256 *----------------------------------------------------------------------
257 */
258
259void
260Tcl_GetTime(timePtr)
261    Tcl_Time *timePtr;		/* Location to store time information. */
262{
263    struct timeb t;
264
265    int useFtime = 1;		/* Flag == TRUE if we need to fall back
266				 * on ftime rather than using the perf
267				 * counter */
268
269    /* Initialize static storage on the first trip through. */
270
271    /*
272     * Note: Outer check for 'initialized' is a performance win
273     * since it avoids an extra mutex lock in the common case.
274     */
275
276    if ( !timeInfo.initialized ) {
277	TclpInitLock();
278	if ( !timeInfo.initialized ) {
279	    timeInfo.perfCounterAvailable
280		= QueryPerformanceFrequency( &timeInfo.nominalFreq );
281
282	    /*
283	     * Some hardware abstraction layers use the CPU clock
284	     * in place of the real-time clock as a performance counter
285	     * reference.  This results in:
286	     *    - inconsistent results among the processors on
287	     *      multi-processor systems.
288	     *    - unpredictable changes in performance counter frequency
289	     *      on "gearshift" processors such as Transmeta and
290	     *      SpeedStep.
291	     *
292	     * There seems to be no way to test whether the performance
293	     * counter is reliable, but a useful heuristic is that
294	     * if its frequency is 1.193182 MHz or 3.579545 MHz, it's
295	     * derived from a colorburst crystal and is therefore
296	     * the RTC rather than the TSC.
297	     *
298	     * A sloppier but serviceable heuristic is that the RTC crystal
299	     * is normally less than 15 MHz while the TSC crystal is
300	     * virtually assured to be greater than 100 MHz.  Since Win98SE
301	     * appears to fiddle with the definition of the perf counter
302	     * frequency (perhaps in an attempt to calibrate the clock?)
303	     * we use the latter rule rather than an exact match.
304	     */
305
306	    if ( timeInfo.perfCounterAvailable
307		 /* The following lines would do an exact match on
308		  * crystal frequency:
309		  * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt) 1193182
310		  * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt) 3579545
311		  */
312		 && timeInfo.nominalFreq.QuadPart > (Tcl_WideInt) 15000000 ) {
313
314		/*
315		 * As an exception, if every logical processor on the system
316		 * is on the same chip, we use the performance counter anyway,
317		 * presuming that everyone's TSC is locked to the same
318		 * oscillator.
319		 */
320
321		SYSTEM_INFO systemInfo;
322		unsigned int regs[4];
323		GetSystemInfo( &systemInfo );
324		if ( TclWinCPUID( 0, regs ) == TCL_OK
325
326		     && regs[1] == 0x756e6547 /* "Genu" */
327		     && regs[3] == 0x49656e69 /* "ineI" */
328		     && regs[2] == 0x6c65746e /* "ntel" */
329
330		     && TclWinCPUID( 1, regs ) == TCL_OK
331
332		     && ( (regs[0] & 0x00000F00) == 0x00000F00 /* Pentium 4 */
333			  || ( (regs[0] & 0x00F00000)    /* Extended family */
334			       && (regs[3] & 0x10000000) ) ) /* Hyperthread */
335		     && ( ( ( regs[1] & 0x00FF0000 ) >> 16 ) /* CPU count */
336			  == systemInfo.dwNumberOfProcessors )
337
338		    ) {
339		    timeInfo.perfCounterAvailable = TRUE;
340		} else {
341		timeInfo.perfCounterAvailable = FALSE;
342	    }
343
344	    }
345
346	    /*
347	     * If the performance counter is available, start a thread to
348	     * calibrate it.
349	     */
350
351	    if ( timeInfo.perfCounterAvailable ) {
352		DWORD id;
353		InitializeCriticalSection( &timeInfo.cs );
354		timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
355		timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
356		timeInfo.calibrationThread = CreateThread( NULL,
357							   256,
358							   CalibrationThread,
359							   (LPVOID) NULL,
360							   0,
361							   &id );
362		SetThreadPriority( timeInfo.calibrationThread,
363				   THREAD_PRIORITY_HIGHEST );
364
365		/*
366		 * Wait for the thread just launched to start running,
367		 * and create an exit handler that kills it so that it
368		 * doesn't outlive unloading tclXX.dll
369		 */
370
371		WaitForSingleObject( timeInfo.readyEvent, INFINITE );
372		CloseHandle( timeInfo.readyEvent );
373		Tcl_CreateExitHandler( StopCalibration, (ClientData) NULL );
374	    }
375	    timeInfo.initialized = TRUE;
376	}
377	TclpInitUnlock();
378    }
379
380    if ( timeInfo.perfCounterAvailable ) {
381	/*
382	 * Query the performance counter and use it to calculate the
383	 * current time.
384	 */
385
386	LARGE_INTEGER curCounter;
387				/* Current performance counter */
388
389	Tcl_WideInt curFileTime;
390				/* Current estimated time, expressed
391				 * as 100-ns ticks since the Windows epoch */
392
393	static LARGE_INTEGER posixEpoch;
394				/* Posix epoch expressed as 100-ns ticks
395				 * since the windows epoch */
396
397	Tcl_WideInt usecSincePosixEpoch;
398				/* Current microseconds since Posix epoch */
399
400	posixEpoch.LowPart = 0xD53E8000;
401	posixEpoch.HighPart = 0x019DB1DE;
402
403	EnterCriticalSection( &timeInfo.cs );
404
405	QueryPerformanceCounter( &curCounter );
406
407	/*
408	 * If it appears to be more than 1.1 seconds since the last trip
409	 * through the calibration loop, the performance counter may
410	 * have jumped forward. (See MSDN Knowledge Base article
411	 * Q274323 for a description of the hardware problem that makes
412	 * this test necessary.) If the counter jumps, we don't want
413	 * to use it directly. Instead, we must return system time.
414	 * Eventually, the calibration loop should recover.
415	 */
416	if ( curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart
417	     < 11 * timeInfo.curCounterFreq.QuadPart / 10 ) {
418
419	    curFileTime = timeInfo.fileTimeLastCall.QuadPart
420		+ ( ( curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart )
421		    * 10000000 / timeInfo.curCounterFreq.QuadPart );
422	    timeInfo.fileTimeLastCall.QuadPart = curFileTime;
423	    timeInfo.perfCounterLastCall.QuadPart = curCounter.QuadPart;
424	    usecSincePosixEpoch = ( curFileTime - posixEpoch.QuadPart ) / 10;
425	    timePtr->sec = (long) ( usecSincePosixEpoch / 1000000 );
426	    timePtr->usec = (unsigned long ) ( usecSincePosixEpoch % 1000000 );
427	    useFtime = 0;
428	}
429
430	LeaveCriticalSection( &timeInfo.cs );
431    }
432
433    if ( useFtime ) {
434	/* High resolution timer is not available.  Just use ftime */
435
436	ftime(&t);
437	timePtr->sec = (long)t.time;
438	timePtr->usec = t.millitm * 1000;
439    }
440}
441
442/*
443 *----------------------------------------------------------------------
444 *
445 * StopCalibration --
446 *
447 *	Turns off the calibration thread in preparation for exiting the
448 *	process.
449 *
450 * Results:
451 *	None.
452 *
453 * Side effects:
454 *	Sets the 'exitEvent' event in the 'timeInfo' structure to ask
455 *	the thread in question to exit, and waits for it to do so.
456 *
457 *----------------------------------------------------------------------
458 */
459
460static void
461StopCalibration( ClientData unused )
462				/* Client data is unused */
463{
464    SetEvent( timeInfo.exitEvent );
465    WaitForSingleObject( timeInfo.calibrationThread, INFINITE );
466    CloseHandle( timeInfo.exitEvent );
467    CloseHandle( timeInfo.calibrationThread );
468}
469
470/*
471 *----------------------------------------------------------------------
472 *
473 * TclpGetTZName --
474 *
475 *	Gets the current timezone string.
476 *
477 * Results:
478 *	Returns a pointer to a static string, or NULL on failure.
479 *
480 * Side effects:
481 *	None.
482 *
483 *----------------------------------------------------------------------
484 */
485
486char *
487TclpGetTZName(int dst)
488{
489    size_t len;
490    char *zone, *p;
491    TIME_ZONE_INFORMATION tz;
492    Tcl_Encoding encoding;
493    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
494    char *name = tsdPtr->tzName;
495
496    /*
497     * tzset() under Borland doesn't seem to set up tzname[] at all.
498     * tzset() under MSVC has the following weird observed behavior:
499     *	 First time we call "clock format [clock seconds] -format %Z -gmt 1"
500     *	 we get "GMT", but on all subsequent calls we get the current time
501     *	 zone string, even though env(TZ) is GMT and the variable _timezone
502     *   is 0.
503     */
504
505    name[0] = '\0';
506
507    zone = getenv("TZ");
508    if (zone != NULL) {
509	/*
510	 * TZ is of form "NST-4:30NDT", where "NST" would be the
511	 * name of the standard time zone for this area, "-4:30" is
512	 * the offset from GMT in hours, and "NDT is the name of
513	 * the daylight savings time zone in this area.  The offset
514	 * and DST strings are optional.
515	 */
516
517	len = strlen(zone);
518	if (len > 3) {
519	    len = 3;
520	}
521	if (dst != 0) {
522	    /*
523	     * Skip the offset string and get the DST string.
524	     */
525
526	    p = zone + len;
527	    p += strspn(p, "+-:0123456789");
528	    if (*p != '\0') {
529		zone = p;
530		len = strlen(zone);
531		if (len > 3) {
532		    len = 3;
533		}
534	    }
535	}
536	Tcl_ExternalToUtf(NULL, NULL, zone, (int)len, 0, NULL, name,
537		sizeof(tsdPtr->tzName), NULL, NULL, NULL);
538    }
539    if (name[0] == '\0') {
540	if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_UNKNOWN) {
541	    /*
542	     * MSDN: On NT this is returned if DST is not used in
543	     * the current TZ
544	     */
545	    dst = 0;
546	}
547	encoding = Tcl_GetEncoding(NULL, "unicode");
548	Tcl_ExternalToUtf(NULL, encoding,
549		(char *) ((dst) ? tz.DaylightName : tz.StandardName), -1,
550		0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL);
551	Tcl_FreeEncoding(encoding);
552    }
553    return name;
554}
555
556/*
557 *----------------------------------------------------------------------
558 *
559 * TclpGetDate --
560 *
561 *	This function converts between seconds and struct tm.  If
562 *	useGMT is true, then the returned date will be in Greenwich
563 *	Mean Time (GMT).  Otherwise, it will be in the local time zone.
564 *
565 * Results:
566 *	Returns a static tm structure.
567 *
568 * Side effects:
569 *	None.
570 *
571 *----------------------------------------------------------------------
572 */
573
574struct tm *
575TclpGetDate(t, useGMT)
576    TclpTime_t t;
577    int useGMT;
578{
579    const time_t *tp = (const time_t *) t;
580    struct tm *tmPtr;
581    time_t time;
582
583    if (!useGMT) {
584	tzset();
585
586	/*
587	 * If we are in the valid range, let the C run-time library
588	 * handle it.  Otherwise we need to fake it.  Note that this
589	 * algorithm ignores daylight savings time before the epoch.
590	 */
591
592	if (*tp >= 0) {
593	    return localtime(tp);
594	}
595
596	time = *tp - _timezone;
597
598	/*
599	 * If we aren't near to overflowing the long, just add the bias and
600	 * use the normal calculation.  Otherwise we will need to adjust
601	 * the result at the end.
602	 */
603
604	if (*tp < (LONG_MAX - 2 * SECSPERDAY)
605		&& *tp > (LONG_MIN + 2 * SECSPERDAY)) {
606	    tmPtr = ComputeGMT(&time);
607	} else {
608	    tmPtr = ComputeGMT(tp);
609
610	    tzset();
611
612	    /*
613	     * Add the bias directly to the tm structure to avoid overflow.
614	     * Propagate seconds overflow into minutes, hours and days.
615	     */
616
617	    time = tmPtr->tm_sec - _timezone;
618	    tmPtr->tm_sec = (int)(time % 60);
619	    if (tmPtr->tm_sec < 0) {
620		tmPtr->tm_sec += 60;
621		time -= 60;
622	    }
623
624	    time = tmPtr->tm_min + time/60;
625	    tmPtr->tm_min = (int)(time % 60);
626	    if (tmPtr->tm_min < 0) {
627		tmPtr->tm_min += 60;
628		time -= 60;
629	    }
630
631	    time = tmPtr->tm_hour + time/60;
632	    tmPtr->tm_hour = (int)(time % 24);
633	    if (tmPtr->tm_hour < 0) {
634		tmPtr->tm_hour += 24;
635		time -= 24;
636	    }
637
638	    time /= 24;
639	    tmPtr->tm_mday += (int)time;
640	    tmPtr->tm_yday += (int)time;
641	    tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7;
642	}
643    } else {
644	tmPtr = ComputeGMT(tp);
645    }
646    return tmPtr;
647}
648
649/*
650 *----------------------------------------------------------------------
651 *
652 * ComputeGMT --
653 *
654 *	This function computes GMT given the number of seconds since
655 *	the epoch (midnight Jan 1 1970).
656 *
657 * Results:
658 *	Returns a (per thread) statically allocated struct tm.
659 *
660 * Side effects:
661 *	Updates the values of the static struct tm.
662 *
663 *----------------------------------------------------------------------
664 */
665
666static struct tm *
667ComputeGMT(tp)
668    const time_t *tp;
669{
670    struct tm *tmPtr;
671    long tmp, rem;
672    int isLeap;
673    int *days;
674    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
675
676    tmPtr = &tsdPtr->tm;
677
678    /*
679     * Compute the 4 year span containing the specified time.
680     */
681
682    tmp = (long)(*tp / SECSPER4YEAR);
683    rem = (LONG)(*tp % SECSPER4YEAR);
684
685    /*
686     * Correct for weird mod semantics so the remainder is always positive.
687     */
688
689    if (rem < 0) {
690	tmp--;
691	rem += SECSPER4YEAR;
692    }
693
694    /*
695     * Compute the year after 1900 by taking the 4 year span and adjusting
696     * for the remainder.  This works because 2000 is a leap year, and
697     * 1900/2100 are out of the range.
698     */
699
700    tmp = (tmp * 4) + 70;
701    isLeap = 0;
702    if (rem >= SECSPERYEAR) {			  /* 1971, etc. */
703	tmp++;
704	rem -= SECSPERYEAR;
705	if (rem >= SECSPERYEAR) {		  /* 1972, etc. */
706	    tmp++;
707	    rem -= SECSPERYEAR;
708	    if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */
709		tmp++;
710		rem -= SECSPERYEAR + SECSPERDAY;
711	    } else {
712		isLeap = 1;
713	    }
714	}
715    }
716    tmPtr->tm_year = tmp;
717
718    /*
719     * Compute the day of year and leave the seconds in the current day in
720     * the remainder.
721     */
722
723    tmPtr->tm_yday = rem / SECSPERDAY;
724    rem %= SECSPERDAY;
725
726    /*
727     * Compute the time of day.
728     */
729
730    tmPtr->tm_hour = rem / 3600;
731    rem %= 3600;
732    tmPtr->tm_min = rem / 60;
733    tmPtr->tm_sec = rem % 60;
734
735    /*
736     * Compute the month and day of month.
737     */
738
739    days = (isLeap) ? leapDays : normalDays;
740    for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) {
741    }
742    tmPtr->tm_mon = --tmp;
743    tmPtr->tm_mday = tmPtr->tm_yday - days[tmp];
744
745    /*
746     * Compute day of week.  Epoch started on a Thursday.
747     */
748
749    tmPtr->tm_wday = (long)(*tp / SECSPERDAY) + 4;
750    if ((*tp % SECSPERDAY) < 0) {
751	tmPtr->tm_wday--;
752    }
753    tmPtr->tm_wday %= 7;
754    if (tmPtr->tm_wday < 0) {
755	tmPtr->tm_wday += 7;
756    }
757
758    return tmPtr;
759}
760
761/*
762 *----------------------------------------------------------------------
763 *
764 * CalibrationThread --
765 *
766 *	Thread that manages calibration of the hi-resolution time
767 *	derived from the performance counter, to keep it synchronized
768 *	with the system clock.
769 *
770 * Parameters:
771 *	arg -- Client data from the CreateThread call.  This parameter
772 *             points to the static TimeInfo structure.
773 *
774 * Return value:
775 *	None.  This thread embeds an infinite loop.
776 *
777 * Side effects:
778 *	At an interval of 1 s, this thread performs virtual time discipline.
779 *
780 * Note: When this thread is entered, TclpInitLock has been called
781 * to safeguard the static storage.  There is therefore no synchronization
782 * in the body of this procedure.
783 *
784 *----------------------------------------------------------------------
785 */
786
787static DWORD WINAPI
788CalibrationThread( LPVOID arg )
789{
790    FILETIME curFileTime;
791    DWORD waitResult;
792
793    /* Get initial system time and performance counter */
794
795    GetSystemTimeAsFileTime( &curFileTime );
796    QueryPerformanceCounter( &timeInfo.perfCounterLastCall );
797    QueryPerformanceFrequency( &timeInfo.curCounterFreq );
798    timeInfo.fileTimeLastCall.LowPart = curFileTime.dwLowDateTime;
799    timeInfo.fileTimeLastCall.HighPart = curFileTime.dwHighDateTime;
800
801    ResetCounterSamples( timeInfo.fileTimeLastCall.QuadPart,
802			 timeInfo.perfCounterLastCall.QuadPart,
803			 timeInfo.curCounterFreq.QuadPart );
804
805    /*
806     * Wake up the calling thread.  When it wakes up, it will release the
807     * initialization lock.
808     */
809
810    SetEvent( timeInfo.readyEvent );
811
812    /* Run the calibration once a second */
813
814    for ( ; ; ) {
815
816	/* If the exitEvent is set, break out of the loop. */
817
818	waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE);
819	if ( waitResult == WAIT_OBJECT_0 ) {
820	    break;
821	}
822	UpdateTimeEachSecond();
823    }
824
825    /* lint */
826    return (DWORD) 0;
827}
828
829/*
830 *----------------------------------------------------------------------
831 *
832 * UpdateTimeEachSecond --
833 *
834 *	Callback from the waitable timer in the clock calibration thread
835 *	that updates system time.
836 *
837 * Parameters:
838 *	info -- Pointer to the static TimeInfo structure
839 *
840 * Results:
841 *	None.
842 *
843 * Side effects:
844 *	Performs virtual time calibration discipline.
845 *
846 *----------------------------------------------------------------------
847 */
848
849static void
850UpdateTimeEachSecond()
851{
852
853    LARGE_INTEGER curPerfCounter;
854				/* Current value returned from
855				 * QueryPerformanceCounter */
856
857    FILETIME curSysTime;	/* Current system time */
858
859    LARGE_INTEGER curFileTime;	/* File time at the time this callback
860				 * was scheduled. */
861
862    Tcl_WideInt estFreq;	/* Estimated perf counter frequency */
863
864    Tcl_WideInt vt0;		/* Tcl time right now */
865    Tcl_WideInt vt1;		/* Tcl time one second from now */
866
867    Tcl_WideInt tdiff;		/* Difference between system clock and
868				 * Tcl time. */
869
870    Tcl_WideInt driftFreq;	/* Frequency needed to drift virtual time
871				 * into step over 1 second */
872
873    /*
874     * Sample performance counter and system time.
875     */
876
877    QueryPerformanceCounter( &curPerfCounter );
878    GetSystemTimeAsFileTime( &curSysTime );
879    curFileTime.LowPart = curSysTime.dwLowDateTime;
880    curFileTime.HighPart = curSysTime.dwHighDateTime;
881
882    EnterCriticalSection( &timeInfo.cs );
883
884    /*
885     * Several things may have gone wrong here that have to
886     * be checked for.
887     * (1) The performance counter may have jumped.
888     * (2) The system clock may have been reset.
889     *
890     * In either case, we'll need to reinitialize the circular buffer
891     * with samples relative to the current system time and the NOMINAL
892     * performance frequency (not the actual, because the actual has
893     * probably run slow in the first case). Our estimated frequency
894     * will be the nominal frequency.
895     */
896
897    /*
898     * Store the current sample into the circular buffer of samples,
899     * and estimate the performance counter frequency.
900     */
901
902    estFreq = AccumulateSample( curPerfCounter.QuadPart,
903				(Tcl_WideUInt) curFileTime.QuadPart );
904
905    /*
906     * We want to adjust things so that time appears to be continuous.
907     * Virtual file time, right now, is
908     *
909     * vt0 = 10000000 * ( curPerfCounter - perfCounterLastCall )
910     *       / curCounterFreq
911     *       + fileTimeLastCall
912     *
913     * Ideally, we would like to drift the clock into place over a
914     * period of 2 sec, so that virtual time 2 sec from now will be
915     *
916     * vt1 = 20000000 + curFileTime
917     *
918     * The frequency that we need to use to drift the counter back into
919     * place is estFreq * 20000000 / ( vt1 - vt0 )
920     */
921
922    vt0 = 10000000 * ( curPerfCounter.QuadPart
923		       - timeInfo.perfCounterLastCall.QuadPart )
924	/ timeInfo.curCounterFreq.QuadPart
925	+ timeInfo.fileTimeLastCall.QuadPart;
926    vt1 = 20000000 + curFileTime.QuadPart;
927
928    /*
929     * If we've gotten more than a second away from system time,
930     * then drifting the clock is going to be pretty hopeless.
931     * Just let it jump. Otherwise, compute the drift frequency and
932     * fill in everything.
933     */
934
935    tdiff = vt0 - curFileTime.QuadPart;
936    if ( tdiff > 10000000 || tdiff < -10000000 ) {
937	timeInfo.fileTimeLastCall.QuadPart = curFileTime.QuadPart;
938	timeInfo.curCounterFreq.QuadPart = estFreq;
939    } else {
940	driftFreq = estFreq * 20000000 / ( vt1 - vt0 );
941	if ( driftFreq > 1003 * estFreq / 1000 ) {
942	    driftFreq = 1003 * estFreq / 1000;
943	}
944	if ( driftFreq < 997 * estFreq / 1000 ) {
945	    driftFreq = 997 * estFreq / 1000;
946	}
947	timeInfo.fileTimeLastCall.QuadPart = vt0;
948	timeInfo.curCounterFreq.QuadPart = driftFreq;
949    }
950
951    timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart;
952
953    LeaveCriticalSection( &timeInfo.cs );
954
955}
956
957/*
958 *----------------------------------------------------------------------
959 *
960 * ResetCounterSamples --
961 *
962 *	Fills the sample arrays in 'timeInfo' with dummy values that will
963 *	yield the current performance counter and frequency.
964 *
965 * Results:
966 *	None.
967 *
968 * Side effects:
969 *	The array of samples is filled in so that it appears that there
970 *	are SAMPLES samples at one-second intervals, separated by precisely
971 *	the given frequency.
972 *
973 *----------------------------------------------------------------------
974 */
975
976static void
977ResetCounterSamples( Tcl_WideUInt fileTime,
978				/* Current file time */
979		     Tcl_WideInt perfCounter,
980				/* Current performance counter */
981		     Tcl_WideInt perfFreq )
982				/* Target performance frequency */
983{
984    int i;
985    for ( i = SAMPLES-1; i >= 0; --i ) {
986	timeInfo.perfCounterSample[i] = perfCounter;
987	timeInfo.fileTimeSample[i] = fileTime;
988	perfCounter -= perfFreq;
989	fileTime -= 10000000;
990    }
991    timeInfo.sampleNo = 0;
992}
993
994/*
995 *----------------------------------------------------------------------
996 *
997 * AccumulateSample --
998 *
999 *	Updates the circular buffer of performance counter and system
1000 *	time samples with a new data point.
1001 *
1002 * Results:
1003 *	None.
1004 *
1005 * Side effects:
1006 *	The new data point replaces the oldest point in the circular
1007 *	buffer, and the descriptive statistics are updated to accumulate
1008 *	the new point.
1009 *
1010 * Several things may have gone wrong here that have to
1011 * be checked for.
1012 * (1) The performance counter may have jumped.
1013 * (2) The system clock may have been reset.
1014 *
1015 * In either case, we'll need to reinitialize the circular buffer
1016 * with samples relative to the current system time and the NOMINAL
1017 * performance frequency (not the actual, because the actual has
1018 * probably run slow in the first case).
1019 */
1020
1021static Tcl_WideInt
1022AccumulateSample( Tcl_WideInt perfCounter,
1023		  Tcl_WideUInt fileTime )
1024{
1025    Tcl_WideUInt workFTSample;	/* File time sample being removed
1026				 * from or added to the circular buffer */
1027
1028    Tcl_WideInt workPCSample;	/* Performance counter sample being
1029				 * removed from or added to the circular
1030				 * buffer */
1031
1032    Tcl_WideUInt lastFTSample;	/* Last file time sample recorded */
1033
1034    Tcl_WideInt lastPCSample;	/* Last performance counter sample recorded */
1035
1036    Tcl_WideInt FTdiff;		/* Difference between last FT and current */
1037
1038    Tcl_WideInt PCdiff;		/* Difference between last PC and current */
1039
1040    Tcl_WideInt estFreq;	/* Estimated performance counter frequency */
1041
1042    /* Test for jumps and reset the samples if we have one. */
1043
1044    if ( timeInfo.sampleNo == 0 ) {
1045	lastPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo
1046						   + SAMPLES - 1 ];
1047	lastFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo
1048						+ SAMPLES - 1 ];
1049    } else {
1050	lastPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo - 1 ];
1051	lastFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo - 1 ];
1052    }
1053    PCdiff = perfCounter - lastPCSample;
1054    FTdiff = fileTime - lastFTSample;
1055    if ( PCdiff < timeInfo.nominalFreq.QuadPart * 9 / 10
1056	 || PCdiff > timeInfo.nominalFreq.QuadPart * 11 / 10
1057	 || FTdiff < 9000000
1058	 || FTdiff > 11000000 ) {
1059	ResetCounterSamples( fileTime, perfCounter,
1060			     timeInfo.nominalFreq.QuadPart );
1061	return timeInfo.nominalFreq.QuadPart;
1062
1063    } else {
1064
1065	/* Estimate the frequency */
1066
1067	workPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo ];
1068	workFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo ];
1069	estFreq = 10000000 * ( perfCounter - workPCSample )
1070	    / ( fileTime - workFTSample );
1071	timeInfo.perfCounterSample[ timeInfo.sampleNo ] = perfCounter;
1072	timeInfo.fileTimeSample[ timeInfo.sampleNo ] = (Tcl_WideInt) fileTime;
1073
1074	/* Advance the sample number */
1075
1076	if ( ++timeInfo.sampleNo >= SAMPLES ) {
1077	    timeInfo.sampleNo = 0;
1078	}
1079
1080	return estFreq;
1081    }
1082}
1083
1084/*
1085 *----------------------------------------------------------------------
1086 *
1087 * TclpGmtime --
1088 *
1089 *	Wrapper around the 'gmtime' library function to make it thread
1090 *	safe.
1091 *
1092 * Results:
1093 *	Returns a pointer to a 'struct tm' in thread-specific data.
1094 *
1095 * Side effects:
1096 *	Invokes gmtime or gmtime_r as appropriate.
1097 *
1098 *----------------------------------------------------------------------
1099 */
1100
1101struct tm *
1102TclpGmtime( tt )
1103    TclpTime_t_CONST tt;
1104{
1105    CONST time_t *timePtr = (CONST time_t *) tt;
1106				/* Pointer to the number of seconds
1107				 * since the local system's epoch */
1108    /*
1109     * The MS implementation of gmtime is thread safe because
1110     * it returns the time in a block of thread-local storage,
1111     * and Windows does not provide a Posix gmtime_r function.
1112     */
1113    return gmtime( timePtr );
1114}
1115
1116/*
1117 *----------------------------------------------------------------------
1118 *
1119 * TclpLocaltime --
1120 *
1121 *	Wrapper around the 'localtime' library function to make it thread
1122 *	safe.
1123 *
1124 * Results:
1125 *	Returns a pointer to a 'struct tm' in thread-specific data.
1126 *
1127 * Side effects:
1128 *	Invokes localtime or localtime_r as appropriate.
1129 *
1130 *----------------------------------------------------------------------
1131 */
1132
1133struct tm *
1134TclpLocaltime( tt )
1135    TclpTime_t_CONST tt;
1136{
1137    CONST time_t *timePtr = (CONST time_t *) tt;
1138				/* Pointer to the number of seconds
1139				 * since the local system's epoch */
1140
1141    /*
1142     * The MS implementation of localtime is thread safe because
1143     * it returns the time in a block of thread-local storage,
1144     * and Windows does not provide a Posix localtime_r function.
1145     */
1146    return localtime( timePtr );
1147}
1148