os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinTime.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinTime.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1147 @@
1.4 +/*
1.5 + * tclWinTime.c --
1.6 + *
1.7 + * Contains Windows specific versions of Tcl functions that
1.8 + * obtain time values from the operating system.
1.9 + *
1.10 + * Copyright 1995-1998 by Sun Microsystems, Inc.
1.11 + *
1.12 + * See the file "license.terms" for information on usage and redistribution
1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.14 + *
1.15 + * RCS: @(#) $Id: tclWinTime.c,v 1.14.2.11 2007/04/21 19:52:15 kennykb Exp $
1.16 + */
1.17 +
1.18 +#include "tclWinInt.h"
1.19 +
1.20 +#define SECSPERDAY (60L * 60L * 24L)
1.21 +#define SECSPERYEAR (SECSPERDAY * 365L)
1.22 +#define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY)
1.23 +
1.24 +/*
1.25 + * Number of samples over which to estimate the performance counter
1.26 + */
1.27 +#define SAMPLES 64
1.28 +
1.29 +/*
1.30 + * The following arrays contain the day of year for the last day of
1.31 + * each month, where index 1 is January.
1.32 + */
1.33 +
1.34 +static int normalDays[] = {
1.35 + -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
1.36 +};
1.37 +
1.38 +static int leapDays[] = {
1.39 + -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
1.40 +};
1.41 +
1.42 +typedef struct ThreadSpecificData {
1.43 + char tzName[64]; /* Time zone name */
1.44 + struct tm tm; /* time information */
1.45 +} ThreadSpecificData;
1.46 +static Tcl_ThreadDataKey dataKey;
1.47 +
1.48 +/*
1.49 + * Data for managing high-resolution timers.
1.50 + */
1.51 +
1.52 +typedef struct TimeInfo {
1.53 +
1.54 + CRITICAL_SECTION cs; /* Mutex guarding this structure */
1.55 +
1.56 + int initialized; /* Flag == 1 if this structure is
1.57 + * initialized. */
1.58 +
1.59 + int perfCounterAvailable; /* Flag == 1 if the hardware has a
1.60 + * performance counter */
1.61 +
1.62 + HANDLE calibrationThread; /* Handle to the thread that keeps the
1.63 + * virtual clock calibrated. */
1.64 +
1.65 + HANDLE readyEvent; /* System event used to
1.66 + * trigger the requesting thread
1.67 + * when the clock calibration procedure
1.68 + * is initialized for the first time */
1.69 +
1.70 + HANDLE exitEvent; /* Event to signal out of an exit handler
1.71 + * to tell the calibration loop to
1.72 + * terminate */
1.73 +
1.74 + LARGE_INTEGER nominalFreq; /* Nominal frequency of the system
1.75 + * performance counter, that is, the value
1.76 + * returned from QueryPerformanceFrequency. */
1.77 +
1.78 + /*
1.79 + * The following values are used for calculating virtual time.
1.80 + * Virtual time is always equal to:
1.81 + * lastFileTime + (current perf counter - lastCounter)
1.82 + * * 10000000 / curCounterFreq
1.83 + * and lastFileTime and lastCounter are updated any time that
1.84 + * virtual time is returned to a caller.
1.85 + */
1.86 +
1.87 + ULARGE_INTEGER fileTimeLastCall;
1.88 + LARGE_INTEGER perfCounterLastCall;
1.89 + LARGE_INTEGER curCounterFreq;
1.90 +
1.91 + /*
1.92 + * Data used in developing the estimate of performance counter
1.93 + * frequency
1.94 + */
1.95 + Tcl_WideUInt fileTimeSample[SAMPLES];
1.96 + /* Last 64 samples of system time */
1.97 + Tcl_WideInt perfCounterSample[SAMPLES];
1.98 + /* Last 64 samples of performance counter */
1.99 + int sampleNo; /* Current sample number */
1.100 +
1.101 +
1.102 +} TimeInfo;
1.103 +
1.104 +static TimeInfo timeInfo = {
1.105 + { NULL },
1.106 + 0,
1.107 + 0,
1.108 + (HANDLE) NULL,
1.109 + (HANDLE) NULL,
1.110 + (HANDLE) NULL,
1.111 +#ifdef HAVE_CAST_TO_UNION
1.112 + (LARGE_INTEGER) (Tcl_WideInt) 0,
1.113 + (ULARGE_INTEGER) (DWORDLONG) 0,
1.114 + (LARGE_INTEGER) (Tcl_WideInt) 0,
1.115 + (LARGE_INTEGER) (Tcl_WideInt) 0,
1.116 +#else
1.117 + 0,
1.118 + 0,
1.119 + 0,
1.120 + 0,
1.121 +#endif
1.122 + { 0 },
1.123 + { 0 },
1.124 + 0
1.125 +};
1.126 +
1.127 +CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
1.128 +
1.129 +/*
1.130 + * Declarations for functions defined later in this file.
1.131 + */
1.132 +
1.133 +static struct tm * ComputeGMT _ANSI_ARGS_((const time_t *tp));
1.134 +static void StopCalibration _ANSI_ARGS_(( ClientData ));
1.135 +static DWORD WINAPI CalibrationThread _ANSI_ARGS_(( LPVOID arg ));
1.136 +static void UpdateTimeEachSecond _ANSI_ARGS_(( void ));
1.137 +static void ResetCounterSamples _ANSI_ARGS_((
1.138 + Tcl_WideUInt fileTime,
1.139 + Tcl_WideInt perfCounter,
1.140 + Tcl_WideInt perfFreq
1.141 + ));
1.142 +static Tcl_WideInt AccumulateSample _ANSI_ARGS_((
1.143 + Tcl_WideInt perfCounter,
1.144 + Tcl_WideUInt fileTime
1.145 + ));
1.146 +
1.147 +/*
1.148 + *----------------------------------------------------------------------
1.149 + *
1.150 + * TclpGetSeconds --
1.151 + *
1.152 + * This procedure returns the number of seconds from the epoch.
1.153 + * On most Unix systems the epoch is Midnight Jan 1, 1970 GMT.
1.154 + *
1.155 + * Results:
1.156 + * Number of seconds from the epoch.
1.157 + *
1.158 + * Side effects:
1.159 + * None.
1.160 + *
1.161 + *----------------------------------------------------------------------
1.162 + */
1.163 +
1.164 +unsigned long
1.165 +TclpGetSeconds()
1.166 +{
1.167 + Tcl_Time t;
1.168 + Tcl_GetTime( &t );
1.169 + return t.sec;
1.170 +}
1.171 +
1.172 +/*
1.173 + *----------------------------------------------------------------------
1.174 + *
1.175 + * TclpGetClicks --
1.176 + *
1.177 + * This procedure returns a value that represents the highest
1.178 + * resolution clock available on the system. There are no
1.179 + * guarantees on what the resolution will be. In Tcl we will
1.180 + * call this value a "click". The start time is also system
1.181 + * dependant.
1.182 + *
1.183 + * Results:
1.184 + * Number of clicks from some start time.
1.185 + *
1.186 + * Side effects:
1.187 + * None.
1.188 + *
1.189 + *----------------------------------------------------------------------
1.190 + */
1.191 +
1.192 +unsigned long
1.193 +TclpGetClicks()
1.194 +{
1.195 + /*
1.196 + * Use the Tcl_GetTime abstraction to get the time in microseconds,
1.197 + * as nearly as we can, and return it.
1.198 + */
1.199 +
1.200 + Tcl_Time now; /* Current Tcl time */
1.201 + unsigned long retval; /* Value to return */
1.202 +
1.203 + Tcl_GetTime( &now );
1.204 + retval = ( now.sec * 1000000 ) + now.usec;
1.205 + return retval;
1.206 +
1.207 +}
1.208 +
1.209 +/*
1.210 + *----------------------------------------------------------------------
1.211 + *
1.212 + * TclpGetTimeZone --
1.213 + *
1.214 + * Determines the current timezone. The method varies wildly
1.215 + * between different Platform implementations, so its hidden in
1.216 + * this function.
1.217 + *
1.218 + * Results:
1.219 + * Minutes west of GMT.
1.220 + *
1.221 + * Side effects:
1.222 + * None.
1.223 + *
1.224 + *----------------------------------------------------------------------
1.225 + */
1.226 +
1.227 +int
1.228 +TclpGetTimeZone (currentTime)
1.229 + Tcl_WideInt currentTime;
1.230 +{
1.231 + int timeZone;
1.232 +
1.233 + tzset();
1.234 + timeZone = _timezone / 60;
1.235 +
1.236 + return timeZone;
1.237 +}
1.238 +
1.239 +/*
1.240 + *----------------------------------------------------------------------
1.241 + *
1.242 + * Tcl_GetTime --
1.243 + *
1.244 + * Gets the current system time in seconds and microseconds
1.245 + * since the beginning of the epoch: 00:00 UCT, January 1, 1970.
1.246 + *
1.247 + * Results:
1.248 + * Returns the current time in timePtr.
1.249 + *
1.250 + * Side effects:
1.251 + * On the first call, initializes a set of static variables to
1.252 + * keep track of the base value of the performance counter, the
1.253 + * corresponding wall clock (obtained through ftime) and the
1.254 + * frequency of the performance counter. Also spins a thread
1.255 + * whose function is to wake up periodically and monitor these
1.256 + * values, adjusting them as necessary to correct for drift
1.257 + * in the performance counter's oscillator.
1.258 + *
1.259 + *----------------------------------------------------------------------
1.260 + */
1.261 +
1.262 +void
1.263 +Tcl_GetTime(timePtr)
1.264 + Tcl_Time *timePtr; /* Location to store time information. */
1.265 +{
1.266 + struct timeb t;
1.267 +
1.268 + int useFtime = 1; /* Flag == TRUE if we need to fall back
1.269 + * on ftime rather than using the perf
1.270 + * counter */
1.271 +
1.272 + /* Initialize static storage on the first trip through. */
1.273 +
1.274 + /*
1.275 + * Note: Outer check for 'initialized' is a performance win
1.276 + * since it avoids an extra mutex lock in the common case.
1.277 + */
1.278 +
1.279 + if ( !timeInfo.initialized ) {
1.280 + TclpInitLock();
1.281 + if ( !timeInfo.initialized ) {
1.282 + timeInfo.perfCounterAvailable
1.283 + = QueryPerformanceFrequency( &timeInfo.nominalFreq );
1.284 +
1.285 + /*
1.286 + * Some hardware abstraction layers use the CPU clock
1.287 + * in place of the real-time clock as a performance counter
1.288 + * reference. This results in:
1.289 + * - inconsistent results among the processors on
1.290 + * multi-processor systems.
1.291 + * - unpredictable changes in performance counter frequency
1.292 + * on "gearshift" processors such as Transmeta and
1.293 + * SpeedStep.
1.294 + *
1.295 + * There seems to be no way to test whether the performance
1.296 + * counter is reliable, but a useful heuristic is that
1.297 + * if its frequency is 1.193182 MHz or 3.579545 MHz, it's
1.298 + * derived from a colorburst crystal and is therefore
1.299 + * the RTC rather than the TSC.
1.300 + *
1.301 + * A sloppier but serviceable heuristic is that the RTC crystal
1.302 + * is normally less than 15 MHz while the TSC crystal is
1.303 + * virtually assured to be greater than 100 MHz. Since Win98SE
1.304 + * appears to fiddle with the definition of the perf counter
1.305 + * frequency (perhaps in an attempt to calibrate the clock?)
1.306 + * we use the latter rule rather than an exact match.
1.307 + */
1.308 +
1.309 + if ( timeInfo.perfCounterAvailable
1.310 + /* The following lines would do an exact match on
1.311 + * crystal frequency:
1.312 + * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt) 1193182
1.313 + * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt) 3579545
1.314 + */
1.315 + && timeInfo.nominalFreq.QuadPart > (Tcl_WideInt) 15000000 ) {
1.316 +
1.317 + /*
1.318 + * As an exception, if every logical processor on the system
1.319 + * is on the same chip, we use the performance counter anyway,
1.320 + * presuming that everyone's TSC is locked to the same
1.321 + * oscillator.
1.322 + */
1.323 +
1.324 + SYSTEM_INFO systemInfo;
1.325 + unsigned int regs[4];
1.326 + GetSystemInfo( &systemInfo );
1.327 + if ( TclWinCPUID( 0, regs ) == TCL_OK
1.328 +
1.329 + && regs[1] == 0x756e6547 /* "Genu" */
1.330 + && regs[3] == 0x49656e69 /* "ineI" */
1.331 + && regs[2] == 0x6c65746e /* "ntel" */
1.332 +
1.333 + && TclWinCPUID( 1, regs ) == TCL_OK
1.334 +
1.335 + && ( (regs[0] & 0x00000F00) == 0x00000F00 /* Pentium 4 */
1.336 + || ( (regs[0] & 0x00F00000) /* Extended family */
1.337 + && (regs[3] & 0x10000000) ) ) /* Hyperthread */
1.338 + && ( ( ( regs[1] & 0x00FF0000 ) >> 16 ) /* CPU count */
1.339 + == systemInfo.dwNumberOfProcessors )
1.340 +
1.341 + ) {
1.342 + timeInfo.perfCounterAvailable = TRUE;
1.343 + } else {
1.344 + timeInfo.perfCounterAvailable = FALSE;
1.345 + }
1.346 +
1.347 + }
1.348 +
1.349 + /*
1.350 + * If the performance counter is available, start a thread to
1.351 + * calibrate it.
1.352 + */
1.353 +
1.354 + if ( timeInfo.perfCounterAvailable ) {
1.355 + DWORD id;
1.356 + InitializeCriticalSection( &timeInfo.cs );
1.357 + timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
1.358 + timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
1.359 + timeInfo.calibrationThread = CreateThread( NULL,
1.360 + 256,
1.361 + CalibrationThread,
1.362 + (LPVOID) NULL,
1.363 + 0,
1.364 + &id );
1.365 + SetThreadPriority( timeInfo.calibrationThread,
1.366 + THREAD_PRIORITY_HIGHEST );
1.367 +
1.368 + /*
1.369 + * Wait for the thread just launched to start running,
1.370 + * and create an exit handler that kills it so that it
1.371 + * doesn't outlive unloading tclXX.dll
1.372 + */
1.373 +
1.374 + WaitForSingleObject( timeInfo.readyEvent, INFINITE );
1.375 + CloseHandle( timeInfo.readyEvent );
1.376 + Tcl_CreateExitHandler( StopCalibration, (ClientData) NULL );
1.377 + }
1.378 + timeInfo.initialized = TRUE;
1.379 + }
1.380 + TclpInitUnlock();
1.381 + }
1.382 +
1.383 + if ( timeInfo.perfCounterAvailable ) {
1.384 + /*
1.385 + * Query the performance counter and use it to calculate the
1.386 + * current time.
1.387 + */
1.388 +
1.389 + LARGE_INTEGER curCounter;
1.390 + /* Current performance counter */
1.391 +
1.392 + Tcl_WideInt curFileTime;
1.393 + /* Current estimated time, expressed
1.394 + * as 100-ns ticks since the Windows epoch */
1.395 +
1.396 + static LARGE_INTEGER posixEpoch;
1.397 + /* Posix epoch expressed as 100-ns ticks
1.398 + * since the windows epoch */
1.399 +
1.400 + Tcl_WideInt usecSincePosixEpoch;
1.401 + /* Current microseconds since Posix epoch */
1.402 +
1.403 + posixEpoch.LowPart = 0xD53E8000;
1.404 + posixEpoch.HighPart = 0x019DB1DE;
1.405 +
1.406 + EnterCriticalSection( &timeInfo.cs );
1.407 +
1.408 + QueryPerformanceCounter( &curCounter );
1.409 +
1.410 + /*
1.411 + * If it appears to be more than 1.1 seconds since the last trip
1.412 + * through the calibration loop, the performance counter may
1.413 + * have jumped forward. (See MSDN Knowledge Base article
1.414 + * Q274323 for a description of the hardware problem that makes
1.415 + * this test necessary.) If the counter jumps, we don't want
1.416 + * to use it directly. Instead, we must return system time.
1.417 + * Eventually, the calibration loop should recover.
1.418 + */
1.419 + if ( curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart
1.420 + < 11 * timeInfo.curCounterFreq.QuadPart / 10 ) {
1.421 +
1.422 + curFileTime = timeInfo.fileTimeLastCall.QuadPart
1.423 + + ( ( curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart )
1.424 + * 10000000 / timeInfo.curCounterFreq.QuadPart );
1.425 + timeInfo.fileTimeLastCall.QuadPart = curFileTime;
1.426 + timeInfo.perfCounterLastCall.QuadPart = curCounter.QuadPart;
1.427 + usecSincePosixEpoch = ( curFileTime - posixEpoch.QuadPart ) / 10;
1.428 + timePtr->sec = (long) ( usecSincePosixEpoch / 1000000 );
1.429 + timePtr->usec = (unsigned long ) ( usecSincePosixEpoch % 1000000 );
1.430 + useFtime = 0;
1.431 + }
1.432 +
1.433 + LeaveCriticalSection( &timeInfo.cs );
1.434 + }
1.435 +
1.436 + if ( useFtime ) {
1.437 + /* High resolution timer is not available. Just use ftime */
1.438 +
1.439 + ftime(&t);
1.440 + timePtr->sec = (long)t.time;
1.441 + timePtr->usec = t.millitm * 1000;
1.442 + }
1.443 +}
1.444 +
1.445 +/*
1.446 + *----------------------------------------------------------------------
1.447 + *
1.448 + * StopCalibration --
1.449 + *
1.450 + * Turns off the calibration thread in preparation for exiting the
1.451 + * process.
1.452 + *
1.453 + * Results:
1.454 + * None.
1.455 + *
1.456 + * Side effects:
1.457 + * Sets the 'exitEvent' event in the 'timeInfo' structure to ask
1.458 + * the thread in question to exit, and waits for it to do so.
1.459 + *
1.460 + *----------------------------------------------------------------------
1.461 + */
1.462 +
1.463 +static void
1.464 +StopCalibration( ClientData unused )
1.465 + /* Client data is unused */
1.466 +{
1.467 + SetEvent( timeInfo.exitEvent );
1.468 + WaitForSingleObject( timeInfo.calibrationThread, INFINITE );
1.469 + CloseHandle( timeInfo.exitEvent );
1.470 + CloseHandle( timeInfo.calibrationThread );
1.471 +}
1.472 +
1.473 +/*
1.474 + *----------------------------------------------------------------------
1.475 + *
1.476 + * TclpGetTZName --
1.477 + *
1.478 + * Gets the current timezone string.
1.479 + *
1.480 + * Results:
1.481 + * Returns a pointer to a static string, or NULL on failure.
1.482 + *
1.483 + * Side effects:
1.484 + * None.
1.485 + *
1.486 + *----------------------------------------------------------------------
1.487 + */
1.488 +
1.489 +char *
1.490 +TclpGetTZName(int dst)
1.491 +{
1.492 + size_t len;
1.493 + char *zone, *p;
1.494 + TIME_ZONE_INFORMATION tz;
1.495 + Tcl_Encoding encoding;
1.496 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.497 + char *name = tsdPtr->tzName;
1.498 +
1.499 + /*
1.500 + * tzset() under Borland doesn't seem to set up tzname[] at all.
1.501 + * tzset() under MSVC has the following weird observed behavior:
1.502 + * First time we call "clock format [clock seconds] -format %Z -gmt 1"
1.503 + * we get "GMT", but on all subsequent calls we get the current time
1.504 + * zone string, even though env(TZ) is GMT and the variable _timezone
1.505 + * is 0.
1.506 + */
1.507 +
1.508 + name[0] = '\0';
1.509 +
1.510 + zone = getenv("TZ");
1.511 + if (zone != NULL) {
1.512 + /*
1.513 + * TZ is of form "NST-4:30NDT", where "NST" would be the
1.514 + * name of the standard time zone for this area, "-4:30" is
1.515 + * the offset from GMT in hours, and "NDT is the name of
1.516 + * the daylight savings time zone in this area. The offset
1.517 + * and DST strings are optional.
1.518 + */
1.519 +
1.520 + len = strlen(zone);
1.521 + if (len > 3) {
1.522 + len = 3;
1.523 + }
1.524 + if (dst != 0) {
1.525 + /*
1.526 + * Skip the offset string and get the DST string.
1.527 + */
1.528 +
1.529 + p = zone + len;
1.530 + p += strspn(p, "+-:0123456789");
1.531 + if (*p != '\0') {
1.532 + zone = p;
1.533 + len = strlen(zone);
1.534 + if (len > 3) {
1.535 + len = 3;
1.536 + }
1.537 + }
1.538 + }
1.539 + Tcl_ExternalToUtf(NULL, NULL, zone, (int)len, 0, NULL, name,
1.540 + sizeof(tsdPtr->tzName), NULL, NULL, NULL);
1.541 + }
1.542 + if (name[0] == '\0') {
1.543 + if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_UNKNOWN) {
1.544 + /*
1.545 + * MSDN: On NT this is returned if DST is not used in
1.546 + * the current TZ
1.547 + */
1.548 + dst = 0;
1.549 + }
1.550 + encoding = Tcl_GetEncoding(NULL, "unicode");
1.551 + Tcl_ExternalToUtf(NULL, encoding,
1.552 + (char *) ((dst) ? tz.DaylightName : tz.StandardName), -1,
1.553 + 0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL);
1.554 + Tcl_FreeEncoding(encoding);
1.555 + }
1.556 + return name;
1.557 +}
1.558 +
1.559 +/*
1.560 + *----------------------------------------------------------------------
1.561 + *
1.562 + * TclpGetDate --
1.563 + *
1.564 + * This function converts between seconds and struct tm. If
1.565 + * useGMT is true, then the returned date will be in Greenwich
1.566 + * Mean Time (GMT). Otherwise, it will be in the local time zone.
1.567 + *
1.568 + * Results:
1.569 + * Returns a static tm structure.
1.570 + *
1.571 + * Side effects:
1.572 + * None.
1.573 + *
1.574 + *----------------------------------------------------------------------
1.575 + */
1.576 +
1.577 +struct tm *
1.578 +TclpGetDate(t, useGMT)
1.579 + TclpTime_t t;
1.580 + int useGMT;
1.581 +{
1.582 + const time_t *tp = (const time_t *) t;
1.583 + struct tm *tmPtr;
1.584 + time_t time;
1.585 +
1.586 + if (!useGMT) {
1.587 + tzset();
1.588 +
1.589 + /*
1.590 + * If we are in the valid range, let the C run-time library
1.591 + * handle it. Otherwise we need to fake it. Note that this
1.592 + * algorithm ignores daylight savings time before the epoch.
1.593 + */
1.594 +
1.595 + if (*tp >= 0) {
1.596 + return localtime(tp);
1.597 + }
1.598 +
1.599 + time = *tp - _timezone;
1.600 +
1.601 + /*
1.602 + * If we aren't near to overflowing the long, just add the bias and
1.603 + * use the normal calculation. Otherwise we will need to adjust
1.604 + * the result at the end.
1.605 + */
1.606 +
1.607 + if (*tp < (LONG_MAX - 2 * SECSPERDAY)
1.608 + && *tp > (LONG_MIN + 2 * SECSPERDAY)) {
1.609 + tmPtr = ComputeGMT(&time);
1.610 + } else {
1.611 + tmPtr = ComputeGMT(tp);
1.612 +
1.613 + tzset();
1.614 +
1.615 + /*
1.616 + * Add the bias directly to the tm structure to avoid overflow.
1.617 + * Propagate seconds overflow into minutes, hours and days.
1.618 + */
1.619 +
1.620 + time = tmPtr->tm_sec - _timezone;
1.621 + tmPtr->tm_sec = (int)(time % 60);
1.622 + if (tmPtr->tm_sec < 0) {
1.623 + tmPtr->tm_sec += 60;
1.624 + time -= 60;
1.625 + }
1.626 +
1.627 + time = tmPtr->tm_min + time/60;
1.628 + tmPtr->tm_min = (int)(time % 60);
1.629 + if (tmPtr->tm_min < 0) {
1.630 + tmPtr->tm_min += 60;
1.631 + time -= 60;
1.632 + }
1.633 +
1.634 + time = tmPtr->tm_hour + time/60;
1.635 + tmPtr->tm_hour = (int)(time % 24);
1.636 + if (tmPtr->tm_hour < 0) {
1.637 + tmPtr->tm_hour += 24;
1.638 + time -= 24;
1.639 + }
1.640 +
1.641 + time /= 24;
1.642 + tmPtr->tm_mday += (int)time;
1.643 + tmPtr->tm_yday += (int)time;
1.644 + tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7;
1.645 + }
1.646 + } else {
1.647 + tmPtr = ComputeGMT(tp);
1.648 + }
1.649 + return tmPtr;
1.650 +}
1.651 +
1.652 +/*
1.653 + *----------------------------------------------------------------------
1.654 + *
1.655 + * ComputeGMT --
1.656 + *
1.657 + * This function computes GMT given the number of seconds since
1.658 + * the epoch (midnight Jan 1 1970).
1.659 + *
1.660 + * Results:
1.661 + * Returns a (per thread) statically allocated struct tm.
1.662 + *
1.663 + * Side effects:
1.664 + * Updates the values of the static struct tm.
1.665 + *
1.666 + *----------------------------------------------------------------------
1.667 + */
1.668 +
1.669 +static struct tm *
1.670 +ComputeGMT(tp)
1.671 + const time_t *tp;
1.672 +{
1.673 + struct tm *tmPtr;
1.674 + long tmp, rem;
1.675 + int isLeap;
1.676 + int *days;
1.677 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.678 +
1.679 + tmPtr = &tsdPtr->tm;
1.680 +
1.681 + /*
1.682 + * Compute the 4 year span containing the specified time.
1.683 + */
1.684 +
1.685 + tmp = (long)(*tp / SECSPER4YEAR);
1.686 + rem = (LONG)(*tp % SECSPER4YEAR);
1.687 +
1.688 + /*
1.689 + * Correct for weird mod semantics so the remainder is always positive.
1.690 + */
1.691 +
1.692 + if (rem < 0) {
1.693 + tmp--;
1.694 + rem += SECSPER4YEAR;
1.695 + }
1.696 +
1.697 + /*
1.698 + * Compute the year after 1900 by taking the 4 year span and adjusting
1.699 + * for the remainder. This works because 2000 is a leap year, and
1.700 + * 1900/2100 are out of the range.
1.701 + */
1.702 +
1.703 + tmp = (tmp * 4) + 70;
1.704 + isLeap = 0;
1.705 + if (rem >= SECSPERYEAR) { /* 1971, etc. */
1.706 + tmp++;
1.707 + rem -= SECSPERYEAR;
1.708 + if (rem >= SECSPERYEAR) { /* 1972, etc. */
1.709 + tmp++;
1.710 + rem -= SECSPERYEAR;
1.711 + if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */
1.712 + tmp++;
1.713 + rem -= SECSPERYEAR + SECSPERDAY;
1.714 + } else {
1.715 + isLeap = 1;
1.716 + }
1.717 + }
1.718 + }
1.719 + tmPtr->tm_year = tmp;
1.720 +
1.721 + /*
1.722 + * Compute the day of year and leave the seconds in the current day in
1.723 + * the remainder.
1.724 + */
1.725 +
1.726 + tmPtr->tm_yday = rem / SECSPERDAY;
1.727 + rem %= SECSPERDAY;
1.728 +
1.729 + /*
1.730 + * Compute the time of day.
1.731 + */
1.732 +
1.733 + tmPtr->tm_hour = rem / 3600;
1.734 + rem %= 3600;
1.735 + tmPtr->tm_min = rem / 60;
1.736 + tmPtr->tm_sec = rem % 60;
1.737 +
1.738 + /*
1.739 + * Compute the month and day of month.
1.740 + */
1.741 +
1.742 + days = (isLeap) ? leapDays : normalDays;
1.743 + for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) {
1.744 + }
1.745 + tmPtr->tm_mon = --tmp;
1.746 + tmPtr->tm_mday = tmPtr->tm_yday - days[tmp];
1.747 +
1.748 + /*
1.749 + * Compute day of week. Epoch started on a Thursday.
1.750 + */
1.751 +
1.752 + tmPtr->tm_wday = (long)(*tp / SECSPERDAY) + 4;
1.753 + if ((*tp % SECSPERDAY) < 0) {
1.754 + tmPtr->tm_wday--;
1.755 + }
1.756 + tmPtr->tm_wday %= 7;
1.757 + if (tmPtr->tm_wday < 0) {
1.758 + tmPtr->tm_wday += 7;
1.759 + }
1.760 +
1.761 + return tmPtr;
1.762 +}
1.763 +
1.764 +/*
1.765 + *----------------------------------------------------------------------
1.766 + *
1.767 + * CalibrationThread --
1.768 + *
1.769 + * Thread that manages calibration of the hi-resolution time
1.770 + * derived from the performance counter, to keep it synchronized
1.771 + * with the system clock.
1.772 + *
1.773 + * Parameters:
1.774 + * arg -- Client data from the CreateThread call. This parameter
1.775 + * points to the static TimeInfo structure.
1.776 + *
1.777 + * Return value:
1.778 + * None. This thread embeds an infinite loop.
1.779 + *
1.780 + * Side effects:
1.781 + * At an interval of 1 s, this thread performs virtual time discipline.
1.782 + *
1.783 + * Note: When this thread is entered, TclpInitLock has been called
1.784 + * to safeguard the static storage. There is therefore no synchronization
1.785 + * in the body of this procedure.
1.786 + *
1.787 + *----------------------------------------------------------------------
1.788 + */
1.789 +
1.790 +static DWORD WINAPI
1.791 +CalibrationThread( LPVOID arg )
1.792 +{
1.793 + FILETIME curFileTime;
1.794 + DWORD waitResult;
1.795 +
1.796 + /* Get initial system time and performance counter */
1.797 +
1.798 + GetSystemTimeAsFileTime( &curFileTime );
1.799 + QueryPerformanceCounter( &timeInfo.perfCounterLastCall );
1.800 + QueryPerformanceFrequency( &timeInfo.curCounterFreq );
1.801 + timeInfo.fileTimeLastCall.LowPart = curFileTime.dwLowDateTime;
1.802 + timeInfo.fileTimeLastCall.HighPart = curFileTime.dwHighDateTime;
1.803 +
1.804 + ResetCounterSamples( timeInfo.fileTimeLastCall.QuadPart,
1.805 + timeInfo.perfCounterLastCall.QuadPart,
1.806 + timeInfo.curCounterFreq.QuadPart );
1.807 +
1.808 + /*
1.809 + * Wake up the calling thread. When it wakes up, it will release the
1.810 + * initialization lock.
1.811 + */
1.812 +
1.813 + SetEvent( timeInfo.readyEvent );
1.814 +
1.815 + /* Run the calibration once a second */
1.816 +
1.817 + for ( ; ; ) {
1.818 +
1.819 + /* If the exitEvent is set, break out of the loop. */
1.820 +
1.821 + waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE);
1.822 + if ( waitResult == WAIT_OBJECT_0 ) {
1.823 + break;
1.824 + }
1.825 + UpdateTimeEachSecond();
1.826 + }
1.827 +
1.828 + /* lint */
1.829 + return (DWORD) 0;
1.830 +}
1.831 +
1.832 +/*
1.833 + *----------------------------------------------------------------------
1.834 + *
1.835 + * UpdateTimeEachSecond --
1.836 + *
1.837 + * Callback from the waitable timer in the clock calibration thread
1.838 + * that updates system time.
1.839 + *
1.840 + * Parameters:
1.841 + * info -- Pointer to the static TimeInfo structure
1.842 + *
1.843 + * Results:
1.844 + * None.
1.845 + *
1.846 + * Side effects:
1.847 + * Performs virtual time calibration discipline.
1.848 + *
1.849 + *----------------------------------------------------------------------
1.850 + */
1.851 +
1.852 +static void
1.853 +UpdateTimeEachSecond()
1.854 +{
1.855 +
1.856 + LARGE_INTEGER curPerfCounter;
1.857 + /* Current value returned from
1.858 + * QueryPerformanceCounter */
1.859 +
1.860 + FILETIME curSysTime; /* Current system time */
1.861 +
1.862 + LARGE_INTEGER curFileTime; /* File time at the time this callback
1.863 + * was scheduled. */
1.864 +
1.865 + Tcl_WideInt estFreq; /* Estimated perf counter frequency */
1.866 +
1.867 + Tcl_WideInt vt0; /* Tcl time right now */
1.868 + Tcl_WideInt vt1; /* Tcl time one second from now */
1.869 +
1.870 + Tcl_WideInt tdiff; /* Difference between system clock and
1.871 + * Tcl time. */
1.872 +
1.873 + Tcl_WideInt driftFreq; /* Frequency needed to drift virtual time
1.874 + * into step over 1 second */
1.875 +
1.876 + /*
1.877 + * Sample performance counter and system time.
1.878 + */
1.879 +
1.880 + QueryPerformanceCounter( &curPerfCounter );
1.881 + GetSystemTimeAsFileTime( &curSysTime );
1.882 + curFileTime.LowPart = curSysTime.dwLowDateTime;
1.883 + curFileTime.HighPart = curSysTime.dwHighDateTime;
1.884 +
1.885 + EnterCriticalSection( &timeInfo.cs );
1.886 +
1.887 + /*
1.888 + * Several things may have gone wrong here that have to
1.889 + * be checked for.
1.890 + * (1) The performance counter may have jumped.
1.891 + * (2) The system clock may have been reset.
1.892 + *
1.893 + * In either case, we'll need to reinitialize the circular buffer
1.894 + * with samples relative to the current system time and the NOMINAL
1.895 + * performance frequency (not the actual, because the actual has
1.896 + * probably run slow in the first case). Our estimated frequency
1.897 + * will be the nominal frequency.
1.898 + */
1.899 +
1.900 + /*
1.901 + * Store the current sample into the circular buffer of samples,
1.902 + * and estimate the performance counter frequency.
1.903 + */
1.904 +
1.905 + estFreq = AccumulateSample( curPerfCounter.QuadPart,
1.906 + (Tcl_WideUInt) curFileTime.QuadPart );
1.907 +
1.908 + /*
1.909 + * We want to adjust things so that time appears to be continuous.
1.910 + * Virtual file time, right now, is
1.911 + *
1.912 + * vt0 = 10000000 * ( curPerfCounter - perfCounterLastCall )
1.913 + * / curCounterFreq
1.914 + * + fileTimeLastCall
1.915 + *
1.916 + * Ideally, we would like to drift the clock into place over a
1.917 + * period of 2 sec, so that virtual time 2 sec from now will be
1.918 + *
1.919 + * vt1 = 20000000 + curFileTime
1.920 + *
1.921 + * The frequency that we need to use to drift the counter back into
1.922 + * place is estFreq * 20000000 / ( vt1 - vt0 )
1.923 + */
1.924 +
1.925 + vt0 = 10000000 * ( curPerfCounter.QuadPart
1.926 + - timeInfo.perfCounterLastCall.QuadPart )
1.927 + / timeInfo.curCounterFreq.QuadPart
1.928 + + timeInfo.fileTimeLastCall.QuadPart;
1.929 + vt1 = 20000000 + curFileTime.QuadPart;
1.930 +
1.931 + /*
1.932 + * If we've gotten more than a second away from system time,
1.933 + * then drifting the clock is going to be pretty hopeless.
1.934 + * Just let it jump. Otherwise, compute the drift frequency and
1.935 + * fill in everything.
1.936 + */
1.937 +
1.938 + tdiff = vt0 - curFileTime.QuadPart;
1.939 + if ( tdiff > 10000000 || tdiff < -10000000 ) {
1.940 + timeInfo.fileTimeLastCall.QuadPart = curFileTime.QuadPart;
1.941 + timeInfo.curCounterFreq.QuadPart = estFreq;
1.942 + } else {
1.943 + driftFreq = estFreq * 20000000 / ( vt1 - vt0 );
1.944 + if ( driftFreq > 1003 * estFreq / 1000 ) {
1.945 + driftFreq = 1003 * estFreq / 1000;
1.946 + }
1.947 + if ( driftFreq < 997 * estFreq / 1000 ) {
1.948 + driftFreq = 997 * estFreq / 1000;
1.949 + }
1.950 + timeInfo.fileTimeLastCall.QuadPart = vt0;
1.951 + timeInfo.curCounterFreq.QuadPart = driftFreq;
1.952 + }
1.953 +
1.954 + timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart;
1.955 +
1.956 + LeaveCriticalSection( &timeInfo.cs );
1.957 +
1.958 +}
1.959 +
1.960 +/*
1.961 + *----------------------------------------------------------------------
1.962 + *
1.963 + * ResetCounterSamples --
1.964 + *
1.965 + * Fills the sample arrays in 'timeInfo' with dummy values that will
1.966 + * yield the current performance counter and frequency.
1.967 + *
1.968 + * Results:
1.969 + * None.
1.970 + *
1.971 + * Side effects:
1.972 + * The array of samples is filled in so that it appears that there
1.973 + * are SAMPLES samples at one-second intervals, separated by precisely
1.974 + * the given frequency.
1.975 + *
1.976 + *----------------------------------------------------------------------
1.977 + */
1.978 +
1.979 +static void
1.980 +ResetCounterSamples( Tcl_WideUInt fileTime,
1.981 + /* Current file time */
1.982 + Tcl_WideInt perfCounter,
1.983 + /* Current performance counter */
1.984 + Tcl_WideInt perfFreq )
1.985 + /* Target performance frequency */
1.986 +{
1.987 + int i;
1.988 + for ( i = SAMPLES-1; i >= 0; --i ) {
1.989 + timeInfo.perfCounterSample[i] = perfCounter;
1.990 + timeInfo.fileTimeSample[i] = fileTime;
1.991 + perfCounter -= perfFreq;
1.992 + fileTime -= 10000000;
1.993 + }
1.994 + timeInfo.sampleNo = 0;
1.995 +}
1.996 +
1.997 +/*
1.998 + *----------------------------------------------------------------------
1.999 + *
1.1000 + * AccumulateSample --
1.1001 + *
1.1002 + * Updates the circular buffer of performance counter and system
1.1003 + * time samples with a new data point.
1.1004 + *
1.1005 + * Results:
1.1006 + * None.
1.1007 + *
1.1008 + * Side effects:
1.1009 + * The new data point replaces the oldest point in the circular
1.1010 + * buffer, and the descriptive statistics are updated to accumulate
1.1011 + * the new point.
1.1012 + *
1.1013 + * Several things may have gone wrong here that have to
1.1014 + * be checked for.
1.1015 + * (1) The performance counter may have jumped.
1.1016 + * (2) The system clock may have been reset.
1.1017 + *
1.1018 + * In either case, we'll need to reinitialize the circular buffer
1.1019 + * with samples relative to the current system time and the NOMINAL
1.1020 + * performance frequency (not the actual, because the actual has
1.1021 + * probably run slow in the first case).
1.1022 + */
1.1023 +
1.1024 +static Tcl_WideInt
1.1025 +AccumulateSample( Tcl_WideInt perfCounter,
1.1026 + Tcl_WideUInt fileTime )
1.1027 +{
1.1028 + Tcl_WideUInt workFTSample; /* File time sample being removed
1.1029 + * from or added to the circular buffer */
1.1030 +
1.1031 + Tcl_WideInt workPCSample; /* Performance counter sample being
1.1032 + * removed from or added to the circular
1.1033 + * buffer */
1.1034 +
1.1035 + Tcl_WideUInt lastFTSample; /* Last file time sample recorded */
1.1036 +
1.1037 + Tcl_WideInt lastPCSample; /* Last performance counter sample recorded */
1.1038 +
1.1039 + Tcl_WideInt FTdiff; /* Difference between last FT and current */
1.1040 +
1.1041 + Tcl_WideInt PCdiff; /* Difference between last PC and current */
1.1042 +
1.1043 + Tcl_WideInt estFreq; /* Estimated performance counter frequency */
1.1044 +
1.1045 + /* Test for jumps and reset the samples if we have one. */
1.1046 +
1.1047 + if ( timeInfo.sampleNo == 0 ) {
1.1048 + lastPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo
1.1049 + + SAMPLES - 1 ];
1.1050 + lastFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo
1.1051 + + SAMPLES - 1 ];
1.1052 + } else {
1.1053 + lastPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo - 1 ];
1.1054 + lastFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo - 1 ];
1.1055 + }
1.1056 + PCdiff = perfCounter - lastPCSample;
1.1057 + FTdiff = fileTime - lastFTSample;
1.1058 + if ( PCdiff < timeInfo.nominalFreq.QuadPart * 9 / 10
1.1059 + || PCdiff > timeInfo.nominalFreq.QuadPart * 11 / 10
1.1060 + || FTdiff < 9000000
1.1061 + || FTdiff > 11000000 ) {
1.1062 + ResetCounterSamples( fileTime, perfCounter,
1.1063 + timeInfo.nominalFreq.QuadPart );
1.1064 + return timeInfo.nominalFreq.QuadPart;
1.1065 +
1.1066 + } else {
1.1067 +
1.1068 + /* Estimate the frequency */
1.1069 +
1.1070 + workPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo ];
1.1071 + workFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo ];
1.1072 + estFreq = 10000000 * ( perfCounter - workPCSample )
1.1073 + / ( fileTime - workFTSample );
1.1074 + timeInfo.perfCounterSample[ timeInfo.sampleNo ] = perfCounter;
1.1075 + timeInfo.fileTimeSample[ timeInfo.sampleNo ] = (Tcl_WideInt) fileTime;
1.1076 +
1.1077 + /* Advance the sample number */
1.1078 +
1.1079 + if ( ++timeInfo.sampleNo >= SAMPLES ) {
1.1080 + timeInfo.sampleNo = 0;
1.1081 + }
1.1082 +
1.1083 + return estFreq;
1.1084 + }
1.1085 +}
1.1086 +
1.1087 +/*
1.1088 + *----------------------------------------------------------------------
1.1089 + *
1.1090 + * TclpGmtime --
1.1091 + *
1.1092 + * Wrapper around the 'gmtime' library function to make it thread
1.1093 + * safe.
1.1094 + *
1.1095 + * Results:
1.1096 + * Returns a pointer to a 'struct tm' in thread-specific data.
1.1097 + *
1.1098 + * Side effects:
1.1099 + * Invokes gmtime or gmtime_r as appropriate.
1.1100 + *
1.1101 + *----------------------------------------------------------------------
1.1102 + */
1.1103 +
1.1104 +struct tm *
1.1105 +TclpGmtime( tt )
1.1106 + TclpTime_t_CONST tt;
1.1107 +{
1.1108 + CONST time_t *timePtr = (CONST time_t *) tt;
1.1109 + /* Pointer to the number of seconds
1.1110 + * since the local system's epoch */
1.1111 + /*
1.1112 + * The MS implementation of gmtime is thread safe because
1.1113 + * it returns the time in a block of thread-local storage,
1.1114 + * and Windows does not provide a Posix gmtime_r function.
1.1115 + */
1.1116 + return gmtime( timePtr );
1.1117 +}
1.1118 +
1.1119 +/*
1.1120 + *----------------------------------------------------------------------
1.1121 + *
1.1122 + * TclpLocaltime --
1.1123 + *
1.1124 + * Wrapper around the 'localtime' library function to make it thread
1.1125 + * safe.
1.1126 + *
1.1127 + * Results:
1.1128 + * Returns a pointer to a 'struct tm' in thread-specific data.
1.1129 + *
1.1130 + * Side effects:
1.1131 + * Invokes localtime or localtime_r as appropriate.
1.1132 + *
1.1133 + *----------------------------------------------------------------------
1.1134 + */
1.1135 +
1.1136 +struct tm *
1.1137 +TclpLocaltime( tt )
1.1138 + TclpTime_t_CONST tt;
1.1139 +{
1.1140 + CONST time_t *timePtr = (CONST time_t *) tt;
1.1141 + /* Pointer to the number of seconds
1.1142 + * since the local system's epoch */
1.1143 +
1.1144 + /*
1.1145 + * The MS implementation of localtime is thread safe because
1.1146 + * it returns the time in a block of thread-local storage,
1.1147 + * and Windows does not provide a Posix localtime_r function.
1.1148 + */
1.1149 + return localtime( timePtr );
1.1150 +}