Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/win/tclWinTime.c @ 68

Last change on this file since 68 was 25, checked in by landauf, 16 years ago

added tcl to libs

File size: 34.4 KB
Line 
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 */
Note: See TracBrowser for help on using the repository browser.