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