OSDN Git Service

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