OSDN Git Service

Updated to tcl 8.4.1
[pf3gnuchains/sourceware.git] / tcl / win / tclWinTime.c
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$
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  * The following arrays contain the day of year for the last day of
23  * each month, where index 1 is January.
24  */
25
26 static int normalDays[] = {
27     -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
28 };
29
30 static int leapDays[] = {
31     -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
32 };
33
34 typedef struct ThreadSpecificData {
35     char tzName[64];            /* Time zone name */
36     struct tm tm;               /* time information */
37 } ThreadSpecificData;
38 static Tcl_ThreadDataKey dataKey;
39
40 /*
41  * Calibration interval for the high-resolution timer, in msec
42  */
43
44 static CONST unsigned long clockCalibrateWakeupInterval = 10000;
45                                 /* FIXME: 10 s -- should be about 10 min! */
46
47 /*
48  * Data for managing high-resolution timers.
49  */
50
51 typedef struct TimeInfo {
52
53     CRITICAL_SECTION cs;        /* Mutex guarding this structure */
54
55     int initialized;            /* Flag == 1 if this structure is
56                                  * initialized. */
57
58     int perfCounterAvailable;   /* Flag == 1 if the hardware has a
59                                  * performance counter */
60
61     HANDLE calibrationThread;   /* Handle to the thread that keeps the
62                                  * virtual clock calibrated. */
63
64     HANDLE readyEvent;          /* System event used to
65                                  * trigger the requesting thread
66                                  * when the clock calibration procedure
67                                  * is initialized for the first time */
68     HANDLE exitEvent;           /* Event to signal out of an exit handler
69                                  * to tell the calibration loop to
70                                  * terminate */
71
72
73     /*
74      * The following values are used for calculating virtual time.
75      * Virtual time is always equal to:
76      *    lastFileTime + (current perf counter - lastCounter) 
77      *                          * 10000000 / curCounterFreq
78      * and lastFileTime and lastCounter are updated any time that
79      * virtual time is returned to a caller.
80      */
81
82     ULARGE_INTEGER lastFileTime;
83     LARGE_INTEGER lastCounter;
84     LARGE_INTEGER curCounterFreq;
85
86     /* 
87      * The next two values are used only in the calibration thread, to track
88      * the frequency of the performance counter.
89      */
90
91     LONGLONG lastPerfCounter;   /* Performance counter the last time
92                                  * that UpdateClockEachSecond was called */
93     LONGLONG lastSysTime;       /* System clock at the last time
94                                  * that UpdateClockEachSecond was called */
95     LONGLONG estPerfCounterFreq;
96                                 /* Current estimate of the counter frequency
97                                  * using the system clock as the standard */
98
99 } TimeInfo;
100
101 static TimeInfo timeInfo = {
102     { NULL }, 
103     0, 
104     0, 
105     (HANDLE) NULL, 
106     (HANDLE) NULL, 
107     (HANDLE) NULL, 
108     0, 
109     0, 
110     0, 
111     0, 
112     0,
113     0
114 };
115
116 CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
117     
118 /*
119  * Declarations for functions defined later in this file.
120  */
121
122 static struct tm *      ComputeGMT _ANSI_ARGS_((const time_t *tp));
123 static void             StopCalibration _ANSI_ARGS_(( ClientData ));
124 static DWORD WINAPI     CalibrationThread _ANSI_ARGS_(( LPVOID arg ));
125 static void             UpdateTimeEachSecond _ANSI_ARGS_(( void ));
126 \f
127 /*
128  *----------------------------------------------------------------------
129  *
130  * TclpGetSeconds --
131  *
132  *      This procedure returns the number of seconds from the epoch.
133  *      On most Unix systems the epoch is Midnight Jan 1, 1970 GMT.
134  *
135  * Results:
136  *      Number of seconds from the epoch.
137  *
138  * Side effects:
139  *      None.
140  *
141  *----------------------------------------------------------------------
142  */
143
144 unsigned long
145 TclpGetSeconds()
146 {
147     Tcl_Time t;
148     Tcl_GetTime( &t );
149     return t.sec;
150 }
151 \f
152 /*
153  *----------------------------------------------------------------------
154  *
155  * TclpGetClicks --
156  *
157  *      This procedure returns a value that represents the highest
158  *      resolution clock available on the system.  There are no
159  *      guarantees on what the resolution will be.  In Tcl we will
160  *      call this value a "click".  The start time is also system
161  *      dependant.
162  *
163  * Results:
164  *      Number of clicks from some start time.
165  *
166  * Side effects:
167  *      None.
168  *
169  *----------------------------------------------------------------------
170  */
171
172 unsigned long
173 TclpGetClicks()
174 {
175     /*
176      * Use the Tcl_GetTime abstraction to get the time in microseconds,
177      * as nearly as we can, and return it.
178      */
179
180     Tcl_Time now;               /* Current Tcl time */
181     unsigned long retval;       /* Value to return */
182
183     Tcl_GetTime( &now );
184     retval = ( now.sec * 1000000 ) + now.usec;
185     return retval;
186
187 }
188 \f
189 /*
190  *----------------------------------------------------------------------
191  *
192  * TclpGetTimeZone --
193  *
194  *      Determines the current timezone.  The method varies wildly
195  *      between different Platform implementations, so its hidden in
196  *      this function.
197  *
198  * Results:
199  *      Minutes west of GMT.
200  *
201  * Side effects:
202  *      None.
203  *
204  *----------------------------------------------------------------------
205  */
206
207 int
208 TclpGetTimeZone (currentTime)
209     unsigned long  currentTime;
210 {
211     int timeZone;
212
213     tzset();
214     timeZone = _timezone / 60;
215
216     return timeZone;
217 }
218 \f
219 /*
220  *----------------------------------------------------------------------
221  *
222  * Tcl_GetTime --
223  *
224  *      Gets the current system time in seconds and microseconds
225  *      since the beginning of the epoch: 00:00 UCT, January 1, 1970.
226  *
227  * Results:
228  *      Returns the current time in timePtr.
229  *
230  * Side effects:
231  *      On the first call, initializes a set of static variables to
232  *      keep track of the base value of the performance counter, the
233  *      corresponding wall clock (obtained through ftime) and the
234  *      frequency of the performance counter.  Also spins a thread
235  *      whose function is to wake up periodically and monitor these
236  *      values, adjusting them as necessary to correct for drift
237  *      in the performance counter's oscillator.
238  *
239  *----------------------------------------------------------------------
240  */
241
242 void
243 Tcl_GetTime(timePtr)
244     Tcl_Time *timePtr;          /* Location to store time information. */
245 {
246         
247     struct timeb t;
248
249     /* Initialize static storage on the first trip through. */
250
251     /*
252      * Note: Outer check for 'initialized' is a performance win
253      * since it avoids an extra mutex lock in the common case.
254      */
255
256     if ( !timeInfo.initialized ) { 
257         TclpInitLock();
258         if ( !timeInfo.initialized ) {
259             timeInfo.perfCounterAvailable
260                 = QueryPerformanceFrequency( &timeInfo.curCounterFreq );
261
262             /*
263              * Some hardware abstraction layers use the CPU clock
264              * in place of the real-time clock as a performance counter
265              * reference.  This results in:
266              *    - inconsistent results among the processors on
267              *      multi-processor systems.
268              *    - unpredictable changes in performance counter frequency
269              *      on "gearshift" processors such as Transmeta and
270              *      SpeedStep.
271              *
272              * There seems to be no way to test whether the performance
273              * counter is reliable, but a useful heuristic is that
274              * if its frequency is 1.193182 MHz or 3.579545 MHz, it's
275              * derived from a colorburst crystal and is therefore
276              * the RTC rather than the TSC.
277              *
278              * A sloppier but serviceable heuristic is that the RTC crystal
279              * is normally less than 15 MHz while the TSC crystal is
280              * virtually assured to be greater than 100 MHz.  Since Win98SE
281              * appears to fiddle with the definition of the perf counter
282              * frequency (perhaps in an attempt to calibrate the clock?)
283              * we use the latter rule rather than an exact match.
284              */
285
286             if ( timeInfo.perfCounterAvailable
287                  /* The following lines would do an exact match on
288                   * crystal frequency:
289                   * && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 1193182
290                   * && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 3579545
291                   */
292                  && timeInfo.curCounterFreq.QuadPart > (LONGLONG) 15000000 ) {
293                 timeInfo.perfCounterAvailable = FALSE;
294             }
295
296             /*
297              * If the performance counter is available, start a thread to
298              * calibrate it.
299              */
300
301             if ( timeInfo.perfCounterAvailable ) {
302                 DWORD id;
303                 InitializeCriticalSection( &timeInfo.cs );
304                 timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
305                 timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
306                 timeInfo.calibrationThread = CreateThread( NULL,
307                                                            8192,
308                                                            CalibrationThread,
309                                                            (LPVOID) NULL,
310                                                            0,
311                                                            &id );
312                 SetThreadPriority( timeInfo.calibrationThread,
313                                    THREAD_PRIORITY_HIGHEST );
314
315                 /*
316                  * Wait for the thread just launched to start running,
317                  * and create an exit handler that kills it so that it
318                  * doesn't outlive unloading tclXX.dll
319                  */
320
321                 WaitForSingleObject( timeInfo.readyEvent, INFINITE );
322                 CloseHandle( timeInfo.readyEvent );
323                 Tcl_CreateExitHandler( StopCalibration, (ClientData) NULL );
324             }
325             timeInfo.initialized = TRUE;
326         }
327         TclpInitUnlock();
328     }
329
330     if ( timeInfo.perfCounterAvailable ) {
331         
332         /*
333          * Query the performance counter and use it to calculate the
334          * current time.
335          */
336
337         LARGE_INTEGER curCounter;
338                                 /* Current performance counter */
339
340         LONGLONG curFileTime;
341                                 /* Current estimated time, expressed
342                                  * as 100-ns ticks since the Windows epoch */
343
344         static LARGE_INTEGER posixEpoch;
345                                 /* Posix epoch expressed as 100-ns ticks
346                                  * since the windows epoch */
347
348         LONGLONG usecSincePosixEpoch;
349                                 /* Current microseconds since Posix epoch */
350
351         posixEpoch.LowPart = 0xD53E8000;
352         posixEpoch.HighPart = 0x019DB1DE;
353
354         EnterCriticalSection( &timeInfo.cs );
355
356         QueryPerformanceCounter( &curCounter );
357         curFileTime = timeInfo.lastFileTime.QuadPart
358             + ( ( curCounter.QuadPart - timeInfo.lastCounter.QuadPart )
359                 * 10000000 / timeInfo.curCounterFreq.QuadPart );
360         timeInfo.lastFileTime.QuadPart = curFileTime;
361         timeInfo.lastCounter.QuadPart = curCounter.QuadPart;
362         usecSincePosixEpoch = ( curFileTime - posixEpoch.QuadPart ) / 10;
363         timePtr->sec = (time_t) ( usecSincePosixEpoch / 1000000 );
364         timePtr->usec = (unsigned long ) ( usecSincePosixEpoch % 1000000 );
365         
366         LeaveCriticalSection( &timeInfo.cs );
367
368         
369     } else {
370         
371         /* High resolution timer is not available.  Just use ftime */
372         
373         ftime(&t);
374         timePtr->sec = t.time;
375         timePtr->usec = t.millitm * 1000;
376     }
377 }
378 \f
379 /*
380  *----------------------------------------------------------------------
381  *
382  * StopCalibration --
383  *
384  *      Turns off the calibration thread in preparation for exiting the
385  *      process.
386  *
387  * Results:
388  *      None.
389  *
390  * Side effects:
391  *      Sets the 'exitEvent' event in the 'timeInfo' structure to ask
392  *      the thread in question to exit, and waits for it to do so.
393  *
394  *----------------------------------------------------------------------
395  */
396
397 static void
398 StopCalibration( ClientData unused )
399                                 /* Client data is unused */
400 {
401     SetEvent( timeInfo.exitEvent );
402     WaitForSingleObject( timeInfo.calibrationThread, INFINITE );
403     CloseHandle( timeInfo.exitEvent );
404     CloseHandle( timeInfo.calibrationThread );
405 }
406 \f
407 /*
408  *----------------------------------------------------------------------
409  *
410  * TclpGetTZName --
411  *
412  *      Gets the current timezone string.
413  *
414  * Results:
415  *      Returns a pointer to a static string, or NULL on failure.
416  *
417  * Side effects:
418  *      None.
419  *
420  *----------------------------------------------------------------------
421  */
422
423 char *
424 TclpGetTZName(int dst)
425 {
426     int len;
427     char *zone, *p;
428     TIME_ZONE_INFORMATION tz;
429     Tcl_Encoding encoding;
430     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
431     char *name = tsdPtr->tzName;
432
433     /*
434      * tzset() under Borland doesn't seem to set up tzname[] at all.
435      * tzset() under MSVC has the following weird observed behavior:
436      *   First time we call "clock format [clock seconds] -format %Z -gmt 1"
437      *   we get "GMT", but on all subsequent calls we get the current time 
438      *   zone string, even though env(TZ) is GMT and the variable _timezone 
439      *   is 0.
440      */
441
442     name[0] = '\0';
443
444     zone = getenv("TZ");
445     if (zone != NULL) {
446         /*
447          * TZ is of form "NST-4:30NDT", where "NST" would be the
448          * name of the standard time zone for this area, "-4:30" is
449          * the offset from GMT in hours, and "NDT is the name of 
450          * the daylight savings time zone in this area.  The offset 
451          * and DST strings are optional.
452          */
453
454         len = strlen(zone);
455         if (len > 3) {
456             len = 3;
457         }
458         if (dst != 0) {
459             /*
460              * Skip the offset string and get the DST string.
461              */
462
463             p = zone + len;
464             p += strspn(p, "+-:0123456789");
465             if (*p != '\0') {
466                 zone = p;
467                 len = strlen(zone);
468                 if (len > 3) {
469                     len = 3;
470                 }
471             }
472         }
473         Tcl_ExternalToUtf(NULL, NULL, zone, len, 0, NULL, name,
474                 sizeof(tsdPtr->tzName), NULL, NULL, NULL);
475     }
476     if (name[0] == '\0') {
477         if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_UNKNOWN) {
478             /*
479              * MSDN: On NT this is returned if DST is not used in
480              * the current TZ
481              */
482             dst = 0;
483         }
484         encoding = Tcl_GetEncoding(NULL, "unicode");
485         Tcl_ExternalToUtf(NULL, encoding, 
486                 (char *) ((dst) ? tz.DaylightName : tz.StandardName), -1, 
487                 0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL);
488         Tcl_FreeEncoding(encoding);
489     } 
490     return name;
491 }
492 \f
493 /*
494  *----------------------------------------------------------------------
495  *
496  * TclpGetDate --
497  *
498  *      This function converts between seconds and struct tm.  If
499  *      useGMT is true, then the returned date will be in Greenwich
500  *      Mean Time (GMT).  Otherwise, it will be in the local time zone.
501  *
502  * Results:
503  *      Returns a static tm structure.
504  *
505  * Side effects:
506  *      None.
507  *
508  *----------------------------------------------------------------------
509  */
510
511 struct tm *
512 TclpGetDate(t, useGMT)
513     TclpTime_t t;
514     int useGMT;
515 {
516     const time_t *tp = (const time_t *) t;
517     struct tm *tmPtr;
518     long time;
519
520     if (!useGMT) {
521         tzset();
522
523         /*
524          * If we are in the valid range, let the C run-time library
525          * handle it.  Otherwise we need to fake it.  Note that this
526          * algorithm ignores daylight savings time before the epoch.
527          */
528
529         if (*tp >= 0) {
530             return localtime(tp);
531         }
532
533         time = *tp - _timezone;
534         
535         /*
536          * If we aren't near to overflowing the long, just add the bias and
537          * use the normal calculation.  Otherwise we will need to adjust
538          * the result at the end.
539          */
540
541         if (*tp < (LONG_MAX - 2 * SECSPERDAY)
542                 && *tp > (LONG_MIN + 2 * SECSPERDAY)) {
543             tmPtr = ComputeGMT(&time);
544         } else {
545             tmPtr = ComputeGMT(tp);
546
547             tzset();
548
549             /*
550              * Add the bias directly to the tm structure to avoid overflow.
551              * Propagate seconds overflow into minutes, hours and days.
552              */
553
554             time = tmPtr->tm_sec - _timezone;
555             tmPtr->tm_sec = (int)(time % 60);
556             if (tmPtr->tm_sec < 0) {
557                 tmPtr->tm_sec += 60;
558                 time -= 60;
559             }
560     
561             time = tmPtr->tm_min + time/60;
562             tmPtr->tm_min = (int)(time % 60);
563             if (tmPtr->tm_min < 0) {
564                 tmPtr->tm_min += 60;
565                 time -= 60;
566             }
567
568             time = tmPtr->tm_hour + time/60;
569             tmPtr->tm_hour = (int)(time % 24);
570             if (tmPtr->tm_hour < 0) {
571                 tmPtr->tm_hour += 24;
572                 time -= 24;
573             }
574
575             time /= 24;
576             tmPtr->tm_mday += time;
577             tmPtr->tm_yday += time;
578             tmPtr->tm_wday = (tmPtr->tm_wday + time) % 7;
579         }
580     } else {
581         tmPtr = ComputeGMT(tp);
582     }
583     return tmPtr;
584 }
585 \f
586 /*
587  *----------------------------------------------------------------------
588  *
589  * ComputeGMT --
590  *
591  *      This function computes GMT given the number of seconds since
592  *      the epoch (midnight Jan 1 1970).
593  *
594  * Results:
595  *      Returns a (per thread) statically allocated struct tm.
596  *
597  * Side effects:
598  *      Updates the values of the static struct tm.
599  *
600  *----------------------------------------------------------------------
601  */
602
603 static struct tm *
604 ComputeGMT(tp)
605     const time_t *tp;
606 {
607     struct tm *tmPtr;
608     long tmp, rem;
609     int isLeap;
610     int *days;
611     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
612
613     tmPtr = &tsdPtr->tm;
614
615     /*
616      * Compute the 4 year span containing the specified time.
617      */
618
619     tmp = *tp / SECSPER4YEAR;
620     rem = *tp % SECSPER4YEAR;
621
622     /*
623      * Correct for weird mod semantics so the remainder is always positive.
624      */
625
626     if (rem < 0) {
627         tmp--;
628         rem += SECSPER4YEAR;
629     }
630
631     /*
632      * Compute the year after 1900 by taking the 4 year span and adjusting
633      * for the remainder.  This works because 2000 is a leap year, and
634      * 1900/2100 are out of the range.
635      */
636
637     tmp = (tmp * 4) + 70;
638     isLeap = 0;
639     if (rem >= SECSPERYEAR) {                     /* 1971, etc. */
640         tmp++;
641         rem -= SECSPERYEAR;
642         if (rem >= SECSPERYEAR) {                 /* 1972, etc. */
643             tmp++;
644             rem -= SECSPERYEAR;
645             if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */
646                 tmp++;
647                 rem -= SECSPERYEAR + SECSPERDAY;
648             } else {
649                 isLeap = 1;
650             }
651         }
652     }
653     tmPtr->tm_year = tmp;
654
655     /*
656      * Compute the day of year and leave the seconds in the current day in
657      * the remainder.
658      */
659
660     tmPtr->tm_yday = rem / SECSPERDAY;
661     rem %= SECSPERDAY;
662     
663     /*
664      * Compute the time of day.
665      */
666
667     tmPtr->tm_hour = rem / 3600;
668     rem %= 3600;
669     tmPtr->tm_min = rem / 60;
670     tmPtr->tm_sec = rem % 60;
671
672     /*
673      * Compute the month and day of month.
674      */
675
676     days = (isLeap) ? leapDays : normalDays;
677     for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) {
678     }
679     tmPtr->tm_mon = --tmp;
680     tmPtr->tm_mday = tmPtr->tm_yday - days[tmp];
681
682     /*
683      * Compute day of week.  Epoch started on a Thursday.
684      */
685
686     tmPtr->tm_wday = (*tp / SECSPERDAY) + 4;
687     if ((*tp % SECSPERDAY) < 0) {
688         tmPtr->tm_wday--;
689     }
690     tmPtr->tm_wday %= 7;
691     if (tmPtr->tm_wday < 0) {
692         tmPtr->tm_wday += 7;
693     }
694
695     return tmPtr;
696 }
697 \f
698 /*
699  *----------------------------------------------------------------------
700  *
701  * CalibrationThread --
702  *
703  *      Thread that manages calibration of the hi-resolution time
704  *      derived from the performance counter, to keep it synchronized
705  *      with the system clock.
706  *
707  * Parameters:
708  *      arg -- Client data from the CreateThread call.  This parameter
709  *             points to the static TimeInfo structure.
710  *
711  * Return value:
712  *      None.  This thread embeds an infinite loop.
713  *
714  * Side effects:
715  *      At an interval of clockCalibrateWakeupInterval ms, this thread
716  *      performs virtual time discipline.
717  *
718  * Note: When this thread is entered, TclpInitLock has been called
719  * to safeguard the static storage.  There is therefore no synchronization
720  * in the body of this procedure.
721  *
722  *----------------------------------------------------------------------
723  */
724
725 static DWORD WINAPI
726 CalibrationThread( LPVOID arg )
727 {
728     FILETIME curFileTime;
729     DWORD waitResult;
730
731     /* Get initial system time and performance counter */
732
733     GetSystemTimeAsFileTime( &curFileTime );
734     QueryPerformanceCounter( &timeInfo.lastCounter );
735     QueryPerformanceFrequency( &timeInfo.curCounterFreq );
736     timeInfo.lastFileTime.LowPart = curFileTime.dwLowDateTime;
737     timeInfo.lastFileTime.HighPart = curFileTime.dwHighDateTime;
738
739     /* Initialize the working storage for the calibration callback */
740
741     timeInfo.lastPerfCounter = timeInfo.lastCounter.QuadPart;
742     timeInfo.estPerfCounterFreq = timeInfo.curCounterFreq.QuadPart;
743
744     /*
745      * Wake up the calling thread.  When it wakes up, it will release the
746      * initialization lock.
747      */
748
749     SetEvent( timeInfo.readyEvent );
750
751     /* Run the calibration once a second */
752
753     for ( ; ; ) {
754
755         /* If the exitEvent is set, break out of the loop. */
756
757         waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE);
758         if ( waitResult == WAIT_OBJECT_0 ) {
759             break;
760         }
761         UpdateTimeEachSecond();
762     }
763
764     /* lint */
765     return (DWORD) 0;
766 }
767 \f
768 /*
769  *----------------------------------------------------------------------
770  *
771  * UpdateTimeEachSecond --
772  *
773  *      Callback from the waitable timer in the clock calibration thread
774  *      that updates system time.
775  *
776  * Parameters:
777  *      info -- Pointer to the static TimeInfo structure
778  *
779  * Results:
780  *      None.
781  *
782  * Side effects:
783  *      Performs virtual time calibration discipline.
784  *
785  *----------------------------------------------------------------------
786  */
787
788 static void
789 UpdateTimeEachSecond()
790 {
791
792     LARGE_INTEGER curPerfCounter;
793                                 /* Current value returned from
794                                  * QueryPerformanceCounter */
795
796     LONGLONG perfCounterDiff;   /* Difference between the current value
797                                  * and the value of 1 second ago */
798
799     FILETIME curSysTime;        /* Current system time */
800
801     LARGE_INTEGER curFileTime;  /* File time at the time this callback
802                                  * was scheduled. */
803
804     LONGLONG fileTimeDiff;      /* Elapsed time on the system clock
805                                  * since the last time this procedure
806                                  * was called */
807
808     LONGLONG instantFreq;       /* Instantaneous estimate of the
809                                  * performance counter frequency */
810
811     LONGLONG delta;             /* Increment to add to the estimated
812                                  * performance counter frequency in the
813                                  * loop filter */
814
815     LONGLONG fuzz;              /* Tolerance for the perf counter frequency */
816
817     LONGLONG lowBound;          /* Lower bound for the frequency assuming
818                                  * 1000 ppm tolerance */
819
820     LONGLONG hiBound;           /* Upper bound for the frequency */
821
822     /*
823      * Get current performance counter and system time.
824      */
825
826     QueryPerformanceCounter( &curPerfCounter );
827     GetSystemTimeAsFileTime( &curSysTime );
828     curFileTime.LowPart = curSysTime.dwLowDateTime;
829     curFileTime.HighPart = curSysTime.dwHighDateTime;
830
831     EnterCriticalSection( &timeInfo.cs );
832
833     /*
834      * Find out how many ticks of the performance counter and the
835      * system clock have elapsed since we got into this procedure.
836      * Estimate the current frequency.
837      */
838
839     perfCounterDiff = curPerfCounter.QuadPart - timeInfo.lastPerfCounter;
840     timeInfo.lastPerfCounter = curPerfCounter.QuadPart;
841     fileTimeDiff = curFileTime.QuadPart - timeInfo.lastSysTime;
842     timeInfo.lastSysTime = curFileTime.QuadPart;
843     instantFreq = ( 10000000 * perfCounterDiff / fileTimeDiff );
844
845     /*
846      * Consider this a timing glitch if instant frequency varies
847      * significantly from the current estimate.
848      */
849
850     fuzz = timeInfo.estPerfCounterFreq >> 10;
851     lowBound = timeInfo.estPerfCounterFreq - fuzz;
852     hiBound = timeInfo.estPerfCounterFreq + fuzz;
853     if ( instantFreq < lowBound || instantFreq > hiBound ) {
854         LeaveCriticalSection( &timeInfo.cs );
855         return;
856     }
857
858     /*
859      * Update the current estimate of performance counter frequency.
860      * This code is equivalent to the loop filter of a phase locked
861      * loop.
862      */
863
864     delta = ( instantFreq - timeInfo.estPerfCounterFreq ) >> 6;
865     timeInfo.estPerfCounterFreq += delta;
866
867     /*
868      * Update the current virtual time.
869      */
870
871     timeInfo.lastFileTime.QuadPart
872         += ( ( curPerfCounter.QuadPart - timeInfo.lastCounter.QuadPart )
873              * 10000000 / timeInfo.curCounterFreq.QuadPart );
874     timeInfo.lastCounter.QuadPart = curPerfCounter.QuadPart;
875
876     delta = curFileTime.QuadPart - timeInfo.lastFileTime.QuadPart;
877     if ( delta > 10000000 || delta < -10000000 ) {
878
879         /*
880          * If the virtual time slip exceeds one second, then adjusting
881          * the counter frequency is hopeless (it'll take over fifteen
882          * minutes to line up with the system clock).  The most likely
883          * cause of this large a slip is a sudden change to the system
884          * clock, perhaps because it was being corrected by wristwatch
885          * and eyeball.  Accept the system time, and set the performance
886          * counter frequency to the current estimate.
887          */
888
889         timeInfo.lastFileTime.QuadPart = curFileTime.QuadPart;
890         timeInfo.curCounterFreq.QuadPart = timeInfo.estPerfCounterFreq;
891
892     } else {
893
894         /*
895          * Compute a counter frequency that will cause virtual time to line
896          * up with system time one second from now, assuming that the
897          * performance counter continues to tick at timeInfo.estPerfCounterFreq.
898          */
899         
900         timeInfo.curCounterFreq.QuadPart
901             = 10000000 * timeInfo.estPerfCounterFreq / ( delta + 10000000 );
902
903         /*
904          * Limit frequency excursions to 1000 ppm from estimate
905          */
906         
907         if ( timeInfo.curCounterFreq.QuadPart < lowBound ) {
908             timeInfo.curCounterFreq.QuadPart = lowBound;
909         } else if ( timeInfo.curCounterFreq.QuadPart > hiBound ) {
910             timeInfo.curCounterFreq.QuadPart = hiBound;
911         }
912     }
913
914     LeaveCriticalSection( &timeInfo.cs );
915
916 }