tclWinTime.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:30k
源码类别:

通讯编程

开发平台:

Visual C++

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