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