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