Import Tcl 8.6.10
This commit is contained in:
470
win/tclWinTime.c
470
win/tclWinTime.c
@@ -51,6 +51,7 @@ typedef struct TimeInfo {
|
||||
* initialized. */
|
||||
int perfCounterAvailable; /* Flag == 1 if the hardware has a performance
|
||||
* counter. */
|
||||
DWORD calibrationInterv; /* Calibration interval in seconds (start 1 sec) */
|
||||
HANDLE calibrationThread; /* Handle to the thread that keeps the virtual
|
||||
* clock calibrated. */
|
||||
HANDLE readyEvent; /* System event used to trigger the requesting
|
||||
@@ -61,7 +62,6 @@ typedef struct TimeInfo {
|
||||
LARGE_INTEGER nominalFreq; /* Nominal frequency of the system performance
|
||||
* counter, that is, the value returned from
|
||||
* QueryPerformanceFrequency. */
|
||||
|
||||
/*
|
||||
* The following values are used for calculating virtual time. Virtual
|
||||
* time is always equal to:
|
||||
@@ -74,6 +74,8 @@ typedef struct TimeInfo {
|
||||
ULARGE_INTEGER fileTimeLastCall;
|
||||
LARGE_INTEGER perfCounterLastCall;
|
||||
LARGE_INTEGER curCounterFreq;
|
||||
LARGE_INTEGER posixEpoch; /* Posix epoch expressed as 100-ns ticks since
|
||||
* the windows epoch. */
|
||||
|
||||
/*
|
||||
* Data used in developing the estimate of performance counter frequency
|
||||
@@ -90,6 +92,7 @@ static TimeInfo timeInfo = {
|
||||
{ NULL, 0, 0, NULL, NULL, 0 },
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
(HANDLE) NULL,
|
||||
(HANDLE) NULL,
|
||||
(HANDLE) NULL,
|
||||
@@ -98,17 +101,30 @@ static TimeInfo timeInfo = {
|
||||
(ULARGE_INTEGER) (DWORDLONG) 0,
|
||||
(LARGE_INTEGER) (Tcl_WideInt) 0,
|
||||
(LARGE_INTEGER) (Tcl_WideInt) 0,
|
||||
(LARGE_INTEGER) (Tcl_WideInt) 0,
|
||||
#else
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
#endif
|
||||
{ 0 },
|
||||
{ 0 },
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* Scale to convert wide click values from the TclpGetWideClicks native
|
||||
* resolution to microsecond resolution and back.
|
||||
*/
|
||||
static struct {
|
||||
int initialized; /* 1 if initialized, 0 otherwise */
|
||||
int perfCounter; /* 1 if performance counter usable for wide clicks */
|
||||
double microsecsScale; /* Denominator scale between clock / microsecs */
|
||||
} wideClick = {0, 0, 0.0};
|
||||
|
||||
|
||||
/*
|
||||
* Declarations for functions defined later in this file.
|
||||
*/
|
||||
@@ -123,6 +139,7 @@ static Tcl_WideInt AccumulateSample(Tcl_WideInt perfCounter,
|
||||
Tcl_WideUInt fileTime);
|
||||
static void NativeScaleTime(Tcl_Time* timebuf,
|
||||
ClientData clientData);
|
||||
static Tcl_WideInt NativeGetMicroseconds(void);
|
||||
static void NativeGetTime(Tcl_Time* timebuf,
|
||||
ClientData clientData);
|
||||
|
||||
@@ -154,10 +171,19 @@ ClientData tclTimeClientData = NULL;
|
||||
unsigned long
|
||||
TclpGetSeconds(void)
|
||||
{
|
||||
Tcl_Time t;
|
||||
Tcl_WideInt usecSincePosixEpoch;
|
||||
|
||||
tclGetTimeProcPtr(&t, tclTimeClientData); /* Tcl_GetTime inlined. */
|
||||
return t.sec;
|
||||
/* Try to use high resolution timer */
|
||||
if ( tclGetTimeProcPtr == NativeGetTime
|
||||
&& (usecSincePosixEpoch = NativeGetMicroseconds())
|
||||
) {
|
||||
return usecSincePosixEpoch / 1000000;
|
||||
} else {
|
||||
Tcl_Time t;
|
||||
|
||||
tclGetTimeProcPtr(&t, tclTimeClientData); /* Tcl_GetTime inlined. */
|
||||
return t.sec;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -168,7 +194,7 @@ TclpGetSeconds(void)
|
||||
* This procedure returns a value that represents the highest resolution
|
||||
* clock available on the system. There are no guarantees on what the
|
||||
* resolution will be. In Tcl we will call this value a "click". The
|
||||
* start time is also system dependant.
|
||||
* start time is also system dependent.
|
||||
*
|
||||
* Results:
|
||||
* Number of clicks from some start time.
|
||||
@@ -182,19 +208,147 @@ TclpGetSeconds(void)
|
||||
unsigned long
|
||||
TclpGetClicks(void)
|
||||
{
|
||||
/*
|
||||
* Use the Tcl_GetTime abstraction to get the time in microseconds, as
|
||||
* nearly as we can, and return it.
|
||||
*/
|
||||
Tcl_WideInt usecSincePosixEpoch;
|
||||
|
||||
Tcl_Time now; /* Current Tcl time */
|
||||
unsigned long retval; /* Value to return */
|
||||
/* Try to use high resolution timer */
|
||||
if ( tclGetTimeProcPtr == NativeGetTime
|
||||
&& (usecSincePosixEpoch = NativeGetMicroseconds())
|
||||
) {
|
||||
return (unsigned long)usecSincePosixEpoch;
|
||||
} else {
|
||||
/*
|
||||
* Use the Tcl_GetTime abstraction to get the time in microseconds, as
|
||||
* nearly as we can, and return it.
|
||||
*/
|
||||
|
||||
tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */
|
||||
Tcl_Time now; /* Current Tcl time */
|
||||
|
||||
retval = (now.sec * 1000000) + now.usec;
|
||||
return retval;
|
||||
tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */
|
||||
return (unsigned long)(now.sec * 1000000) + now.usec;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclpGetWideClicks --
|
||||
*
|
||||
* This procedure returns a WideInt value that represents the highest
|
||||
* resolution clock in microseconds available on the system.
|
||||
*
|
||||
* Results:
|
||||
* Number of microseconds (from some start time).
|
||||
*
|
||||
* Side effects:
|
||||
* This should be used for time-delta resp. for measurement purposes
|
||||
* only, because on some platforms can return microseconds from some
|
||||
* start time (not from the epoch).
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
Tcl_WideInt
|
||||
TclpGetWideClicks(void)
|
||||
{
|
||||
LARGE_INTEGER curCounter;
|
||||
|
||||
if (!wideClick.initialized) {
|
||||
LARGE_INTEGER perfCounterFreq;
|
||||
|
||||
/*
|
||||
* The frequency of the performance counter is fixed at system boot and
|
||||
* is consistent across all processors. Therefore, the frequency need
|
||||
* only be queried upon application initialization.
|
||||
*/
|
||||
if (QueryPerformanceFrequency(&perfCounterFreq)) {
|
||||
wideClick.perfCounter = 1;
|
||||
wideClick.microsecsScale = 1000000.0 / perfCounterFreq.QuadPart;
|
||||
} else {
|
||||
/* fallback using microseconds */
|
||||
wideClick.perfCounter = 0;
|
||||
wideClick.microsecsScale = 1;
|
||||
}
|
||||
|
||||
wideClick.initialized = 1;
|
||||
}
|
||||
if (wideClick.perfCounter) {
|
||||
if (QueryPerformanceCounter(&curCounter)) {
|
||||
return (Tcl_WideInt)curCounter.QuadPart;
|
||||
}
|
||||
/* fallback using microseconds */
|
||||
wideClick.perfCounter = 0;
|
||||
wideClick.microsecsScale = 1;
|
||||
return TclpGetMicroseconds();
|
||||
} else {
|
||||
return TclpGetMicroseconds();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclpWideClickInMicrosec --
|
||||
*
|
||||
* This procedure return scale to convert wide click values from the
|
||||
* TclpGetWideClicks native resolution to microsecond resolution
|
||||
* and back.
|
||||
*
|
||||
* Results:
|
||||
* 1 click in microseconds as double.
|
||||
*
|
||||
* Side effects:
|
||||
* None.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
double
|
||||
TclpWideClickInMicrosec(void)
|
||||
{
|
||||
if (!wideClick.initialized) {
|
||||
(void)TclpGetWideClicks(); /* initialize */
|
||||
}
|
||||
return wideClick.microsecsScale;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclpGetMicroseconds --
|
||||
*
|
||||
* This procedure returns a WideInt value that represents the highest
|
||||
* resolution clock in microseconds available on the system.
|
||||
*
|
||||
* Results:
|
||||
* Number of microseconds (from the epoch).
|
||||
*
|
||||
* Side effects:
|
||||
* None.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
Tcl_WideInt
|
||||
TclpGetMicroseconds(void)
|
||||
{
|
||||
Tcl_WideInt usecSincePosixEpoch;
|
||||
|
||||
/* Try to use high resolution timer */
|
||||
if ( tclGetTimeProcPtr == NativeGetTime
|
||||
&& (usecSincePosixEpoch = NativeGetMicroseconds())
|
||||
) {
|
||||
return usecSincePosixEpoch;
|
||||
} else {
|
||||
/*
|
||||
* Use the Tcl_GetTime abstraction to get the time in microseconds, as
|
||||
* nearly as we can, and return it.
|
||||
*/
|
||||
|
||||
Tcl_Time now;
|
||||
|
||||
tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */
|
||||
return (((Tcl_WideInt)now.sec) * 1000000) + now.usec;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -223,7 +377,17 @@ void
|
||||
Tcl_GetTime(
|
||||
Tcl_Time *timePtr) /* Location to store time information. */
|
||||
{
|
||||
tclGetTimeProcPtr(timePtr, tclTimeClientData);
|
||||
Tcl_WideInt usecSincePosixEpoch;
|
||||
|
||||
/* Try to use high resolution timer */
|
||||
if ( tclGetTimeProcPtr == NativeGetTime
|
||||
&& (usecSincePosixEpoch = NativeGetMicroseconds())
|
||||
) {
|
||||
timePtr->sec = (long) (usecSincePosixEpoch / 1000000);
|
||||
timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000);
|
||||
} else {
|
||||
tclGetTimeProcPtr(timePtr, tclTimeClientData);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -256,13 +420,14 @@ NativeScaleTime(
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* NativeGetTime --
|
||||
* NativeGetMicroseconds --
|
||||
*
|
||||
* TIP #233: Gets the current system time in seconds and microseconds
|
||||
* since the beginning of the epoch: 00:00 UCT, January 1, 1970.
|
||||
* Gets the current system time in microseconds since the beginning
|
||||
* of the epoch: 00:00 UCT, January 1, 1970.
|
||||
*
|
||||
* Results:
|
||||
* Returns the current time in timePtr.
|
||||
* Returns the wide integer with number of microseconds from the epoch, or
|
||||
* 0 if high resolution timer is not available.
|
||||
*
|
||||
* Side effects:
|
||||
* On the first call, initializes a set of static variables to keep track
|
||||
@@ -275,13 +440,20 @@ NativeScaleTime(
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void
|
||||
NativeGetTime(
|
||||
Tcl_Time *timePtr,
|
||||
ClientData clientData)
|
||||
{
|
||||
struct _timeb t;
|
||||
static inline Tcl_WideInt
|
||||
NativeCalc100NsTicks(
|
||||
ULONGLONG fileTimeLastCall,
|
||||
LONGLONG perfCounterLastCall,
|
||||
LONGLONG curCounterFreq,
|
||||
LONGLONG curCounter
|
||||
) {
|
||||
return fileTimeLastCall +
|
||||
((curCounter - perfCounterLastCall) * 10000000 / curCounterFreq);
|
||||
}
|
||||
|
||||
static Tcl_WideInt
|
||||
NativeGetMicroseconds(void)
|
||||
{
|
||||
/*
|
||||
* Initialize static storage on the first trip through.
|
||||
*
|
||||
@@ -292,6 +464,10 @@ NativeGetTime(
|
||||
if (!timeInfo.initialized) {
|
||||
TclpInitLock();
|
||||
if (!timeInfo.initialized) {
|
||||
|
||||
timeInfo.posixEpoch.LowPart = 0xD53E8000;
|
||||
timeInfo.posixEpoch.HighPart = 0x019DB1DE;
|
||||
|
||||
timeInfo.perfCounterAvailable =
|
||||
QueryPerformanceFrequency(&timeInfo.nominalFreq);
|
||||
|
||||
@@ -368,8 +544,8 @@ NativeGetTime(
|
||||
DWORD id;
|
||||
|
||||
InitializeCriticalSection(&timeInfo.cs);
|
||||
timeInfo.readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
timeInfo.exitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
timeInfo.readyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||
timeInfo.exitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||
timeInfo.calibrationThread = CreateThread(NULL, 256,
|
||||
CalibrationThread, (LPVOID) NULL, 0, &id);
|
||||
SetThreadPriority(timeInfo.calibrationThread,
|
||||
@@ -396,22 +572,12 @@ NativeGetTime(
|
||||
* time.
|
||||
*/
|
||||
|
||||
ULARGE_INTEGER fileTimeLastCall;
|
||||
LARGE_INTEGER perfCounterLastCall, curCounterFreq;
|
||||
ULONGLONG fileTimeLastCall;
|
||||
LONGLONG perfCounterLastCall, curCounterFreq;
|
||||
/* Copy with current data of calibration cycle */
|
||||
|
||||
LARGE_INTEGER curCounter;
|
||||
/* Current performance counter. */
|
||||
Tcl_WideInt curFileTime;/* Current estimated time, expressed as 100-ns
|
||||
* ticks since the Windows epoch. */
|
||||
static LARGE_INTEGER posixEpoch;
|
||||
/* Posix epoch expressed as 100-ns ticks since
|
||||
* the windows epoch. */
|
||||
Tcl_WideInt usecSincePosixEpoch;
|
||||
/* Current microseconds since Posix epoch. */
|
||||
|
||||
posixEpoch.LowPart = 0xD53E8000;
|
||||
posixEpoch.HighPart = 0x019DB1DE;
|
||||
|
||||
QueryPerformanceCounter(&curCounter);
|
||||
|
||||
@@ -420,21 +586,18 @@ NativeGetTime(
|
||||
*/
|
||||
EnterCriticalSection(&timeInfo.cs);
|
||||
|
||||
fileTimeLastCall.QuadPart = timeInfo.fileTimeLastCall.QuadPart;
|
||||
perfCounterLastCall.QuadPart = timeInfo.perfCounterLastCall.QuadPart;
|
||||
curCounterFreq.QuadPart = timeInfo.curCounterFreq.QuadPart;
|
||||
fileTimeLastCall = timeInfo.fileTimeLastCall.QuadPart;
|
||||
perfCounterLastCall = timeInfo.perfCounterLastCall.QuadPart;
|
||||
curCounterFreq = timeInfo.curCounterFreq.QuadPart;
|
||||
|
||||
LeaveCriticalSection(&timeInfo.cs);
|
||||
|
||||
/*
|
||||
* If calibration cycle occurred after we get curCounter
|
||||
*/
|
||||
if (curCounter.QuadPart <= perfCounterLastCall.QuadPart) {
|
||||
usecSincePosixEpoch =
|
||||
(fileTimeLastCall.QuadPart - posixEpoch.QuadPart) / 10;
|
||||
timePtr->sec = (long) (usecSincePosixEpoch / 1000000);
|
||||
timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000);
|
||||
return;
|
||||
if (curCounter.QuadPart <= perfCounterLastCall) {
|
||||
/* Calibrated file-time is saved from posix in 100-ns ticks */
|
||||
return fileTimeLastCall / 10;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -447,27 +610,62 @@ NativeGetTime(
|
||||
* loop should recover.
|
||||
*/
|
||||
|
||||
if (curCounter.QuadPart - perfCounterLastCall.QuadPart <
|
||||
11 * curCounterFreq.QuadPart / 10
|
||||
if (curCounter.QuadPart - perfCounterLastCall <
|
||||
11 * curCounterFreq * timeInfo.calibrationInterv / 10
|
||||
) {
|
||||
curFileTime = fileTimeLastCall.QuadPart +
|
||||
((curCounter.QuadPart - perfCounterLastCall.QuadPart)
|
||||
* 10000000 / curCounterFreq.QuadPart);
|
||||
|
||||
usecSincePosixEpoch = (curFileTime - posixEpoch.QuadPart) / 10;
|
||||
timePtr->sec = (long) (usecSincePosixEpoch / 1000000);
|
||||
timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000);
|
||||
return;
|
||||
/* Calibrated file-time is saved from posix in 100-ns ticks */
|
||||
return NativeCalc100NsTicks(fileTimeLastCall,
|
||||
perfCounterLastCall, curCounterFreq, curCounter.QuadPart) / 10;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* High resolution timer is not available. Just use ftime.
|
||||
* High resolution timer is not available.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* NativeGetTime --
|
||||
*
|
||||
* TIP #233: Gets the current system time in seconds and microseconds
|
||||
* since the beginning of the epoch: 00:00 UCT, January 1, 1970.
|
||||
*
|
||||
* Results:
|
||||
* Returns the current time in timePtr.
|
||||
*
|
||||
* Side effects:
|
||||
* See NativeGetMicroseconds for more information.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
_ftime(&t);
|
||||
timePtr->sec = (long)t.time;
|
||||
timePtr->usec = t.millitm * 1000;
|
||||
static void
|
||||
NativeGetTime(
|
||||
Tcl_Time *timePtr,
|
||||
ClientData clientData)
|
||||
{
|
||||
Tcl_WideInt usecSincePosixEpoch;
|
||||
|
||||
/*
|
||||
* Try to use high resolution timer.
|
||||
*/
|
||||
if ( (usecSincePosixEpoch = NativeGetMicroseconds()) ) {
|
||||
timePtr->sec = (long) (usecSincePosixEpoch / 1000000);
|
||||
timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000);
|
||||
} else {
|
||||
/*
|
||||
* High resolution timer is not available. Just use ftime.
|
||||
*/
|
||||
|
||||
struct _timeb t;
|
||||
|
||||
_ftime(&t);
|
||||
timePtr->sec = (long)t.time;
|
||||
timePtr->usec = t.millitm * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -488,6 +686,8 @@ NativeGetTime(
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void TclWinResetTimerResolution(void);
|
||||
|
||||
static void
|
||||
StopCalibration(
|
||||
ClientData unused) /* Client data is unused */
|
||||
@@ -529,8 +729,18 @@ TclpGetDate(
|
||||
{
|
||||
struct tm *tmPtr;
|
||||
time_t time;
|
||||
#if defined(_WIN64) || (defined(_USE_64BIT_TIME_T) || (defined(_MSC_VER) && _MSC_VER < 1400))
|
||||
# define t2 *t /* no need to cripple time to 32-bit */
|
||||
#else
|
||||
time_t t2 = *(__time32_t *)t;
|
||||
#endif
|
||||
|
||||
if (!useGMT) {
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1900)
|
||||
# undef timezone /* prevent conflict with timezone() function */
|
||||
long timezone = 0;
|
||||
#endif
|
||||
|
||||
tzset();
|
||||
|
||||
/*
|
||||
@@ -556,11 +766,15 @@ TclpGetDate(
|
||||
#define LOCALTIME_VALIDITY_BOUNDARY 0
|
||||
#endif
|
||||
|
||||
if (*t >= LOCALTIME_VALIDITY_BOUNDARY) {
|
||||
return TclpLocaltime(t);
|
||||
if (t2 >= LOCALTIME_VALIDITY_BOUNDARY) {
|
||||
return TclpLocaltime(&t2);
|
||||
}
|
||||
|
||||
time = *t - timezone;
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1900)
|
||||
_get_timezone(&timezone);
|
||||
#endif
|
||||
|
||||
time = t2 - timezone;
|
||||
|
||||
/*
|
||||
* If we aren't near to overflowing the long, just add the bias and
|
||||
@@ -568,10 +782,10 @@ TclpGetDate(
|
||||
* result at the end.
|
||||
*/
|
||||
|
||||
if (*t < (LONG_MAX - 2*SECSPERDAY) && *t > (LONG_MIN + 2*SECSPERDAY)) {
|
||||
if (t2 < (LONG_MAX - 2*SECSPERDAY) && t2 > (LONG_MIN + 2*SECSPERDAY)) {
|
||||
tmPtr = ComputeGMT(&time);
|
||||
} else {
|
||||
tmPtr = ComputeGMT(t);
|
||||
tmPtr = ComputeGMT(&t2);
|
||||
|
||||
tzset();
|
||||
|
||||
@@ -607,7 +821,7 @@ TclpGetDate(
|
||||
tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7;
|
||||
}
|
||||
} else {
|
||||
tmPtr = ComputeGMT(t);
|
||||
tmPtr = ComputeGMT(&t2);
|
||||
}
|
||||
return tmPtr;
|
||||
}
|
||||
@@ -767,6 +981,8 @@ CalibrationThread(
|
||||
QueryPerformanceFrequency(&timeInfo.curCounterFreq);
|
||||
timeInfo.fileTimeLastCall.LowPart = curFileTime.dwLowDateTime;
|
||||
timeInfo.fileTimeLastCall.HighPart = curFileTime.dwHighDateTime;
|
||||
/* Calibrated file-time will be saved from posix in 100-ns ticks */
|
||||
timeInfo.fileTimeLastCall.QuadPart -= timeInfo.posixEpoch.QuadPart;
|
||||
|
||||
ResetCounterSamples(timeInfo.fileTimeLastCall.QuadPart,
|
||||
timeInfo.perfCounterLastCall.QuadPart,
|
||||
@@ -826,6 +1042,7 @@ UpdateTimeEachSecond(void)
|
||||
/* Current value returned from
|
||||
* QueryPerformanceCounter. */
|
||||
FILETIME curSysTime; /* Current system time. */
|
||||
static LARGE_INTEGER lastFileTime; /* File time of the previous calibration */
|
||||
LARGE_INTEGER curFileTime; /* File time at the time this callback was
|
||||
* scheduled. */
|
||||
Tcl_WideInt estFreq; /* Estimated perf counter frequency. */
|
||||
@@ -837,15 +1054,24 @@ UpdateTimeEachSecond(void)
|
||||
* step over 1 second. */
|
||||
|
||||
/*
|
||||
* Sample performance counter and system time.
|
||||
* Sample performance counter and system time (from posix epoch).
|
||||
*/
|
||||
|
||||
QueryPerformanceCounter(&curPerfCounter);
|
||||
GetSystemTimeAsFileTime(&curSysTime);
|
||||
curFileTime.LowPart = curSysTime.dwLowDateTime;
|
||||
curFileTime.HighPart = curSysTime.dwHighDateTime;
|
||||
curFileTime.QuadPart -= timeInfo.posixEpoch.QuadPart;
|
||||
/* If calibration still not needed (check for possible time switch) */
|
||||
if ( curFileTime.QuadPart > lastFileTime.QuadPart
|
||||
&& curFileTime.QuadPart < lastFileTime.QuadPart +
|
||||
(timeInfo.calibrationInterv * 10000000)
|
||||
) {
|
||||
/* again in next one second */
|
||||
return;
|
||||
}
|
||||
QueryPerformanceCounter(&curPerfCounter);
|
||||
|
||||
EnterCriticalSection(&timeInfo.cs);
|
||||
lastFileTime.QuadPart = curFileTime.QuadPart;
|
||||
|
||||
/*
|
||||
* We devide by timeInfo.curCounterFreq.QuadPart in several places. That
|
||||
@@ -857,7 +1083,6 @@ UpdateTimeEachSecond(void)
|
||||
*/
|
||||
|
||||
if (timeInfo.curCounterFreq.QuadPart == 0){
|
||||
LeaveCriticalSection(&timeInfo.cs);
|
||||
timeInfo.perfCounterAvailable = 0;
|
||||
return;
|
||||
}
|
||||
@@ -896,12 +1121,9 @@ UpdateTimeEachSecond(void)
|
||||
* is estFreq * 20000000 / (vt1 - vt0)
|
||||
*/
|
||||
|
||||
vt0 = 10000000 * (curPerfCounter.QuadPart
|
||||
- timeInfo.perfCounterLastCall.QuadPart)
|
||||
/ timeInfo.curCounterFreq.QuadPart
|
||||
+ timeInfo.fileTimeLastCall.QuadPart;
|
||||
vt1 = 20000000 + curFileTime.QuadPart;
|
||||
|
||||
vt0 = NativeCalc100NsTicks(timeInfo.fileTimeLastCall.QuadPart,
|
||||
timeInfo.perfCounterLastCall.QuadPart, timeInfo.curCounterFreq.QuadPart,
|
||||
curPerfCounter.QuadPart);
|
||||
/*
|
||||
* If we've gotten more than a second away from system time, then drifting
|
||||
* the clock is going to be pretty hopeless. Just let it jump. Otherwise,
|
||||
@@ -910,21 +1132,75 @@ UpdateTimeEachSecond(void)
|
||||
|
||||
tdiff = vt0 - curFileTime.QuadPart;
|
||||
if (tdiff > 10000000 || tdiff < -10000000) {
|
||||
timeInfo.fileTimeLastCall.QuadPart = curFileTime.QuadPart;
|
||||
timeInfo.curCounterFreq.QuadPart = estFreq;
|
||||
/* jump to current system time, use curent estimated frequency */
|
||||
vt0 = curFileTime.QuadPart;
|
||||
} else {
|
||||
driftFreq = estFreq * 20000000 / (vt1 - vt0);
|
||||
/* calculate new frequency and estimate drift to the next second */
|
||||
vt1 = 20000000 + curFileTime.QuadPart;
|
||||
driftFreq = (estFreq * 20000000 / (vt1 - vt0));
|
||||
/*
|
||||
* Avoid too large drifts (only half of the current difference),
|
||||
* that allows also be more accurate (aspire to the smallest tdiff),
|
||||
* so then we can prolong calibration interval by tdiff < 100000
|
||||
*/
|
||||
driftFreq = timeInfo.curCounterFreq.QuadPart +
|
||||
(driftFreq - timeInfo.curCounterFreq.QuadPart) / 2;
|
||||
|
||||
if (driftFreq > 1003*estFreq/1000) {
|
||||
driftFreq = 1003*estFreq/1000;
|
||||
} else if (driftFreq < 997*estFreq/1000) {
|
||||
driftFreq = 997*estFreq/1000;
|
||||
}
|
||||
|
||||
timeInfo.fileTimeLastCall.QuadPart = vt0;
|
||||
timeInfo.curCounterFreq.QuadPart = driftFreq;
|
||||
/*
|
||||
* Average between estimated, 2 current and 5 drifted frequencies,
|
||||
* (do the soft drifting as possible)
|
||||
*/
|
||||
estFreq = (estFreq + 2 * timeInfo.curCounterFreq.QuadPart + 5 * driftFreq) / 8;
|
||||
}
|
||||
|
||||
/* Avoid too large discrepancy from nominal frequency */
|
||||
if (estFreq > 1003*timeInfo.nominalFreq.QuadPart/1000) {
|
||||
estFreq = 1003*timeInfo.nominalFreq.QuadPart/1000;
|
||||
vt0 = curFileTime.QuadPart;
|
||||
} else if (estFreq < 997*timeInfo.nominalFreq.QuadPart/1000) {
|
||||
estFreq = 997*timeInfo.nominalFreq.QuadPart/1000;
|
||||
vt0 = curFileTime.QuadPart;
|
||||
} else if (vt0 != curFileTime.QuadPart) {
|
||||
/*
|
||||
* Be sure the clock ticks never backwards (avoid it by negative drifting)
|
||||
* just compare native time (in 100-ns) before and hereafter using
|
||||
* new calibrated values) and do a small adjustment (short time freeze)
|
||||
*/
|
||||
LARGE_INTEGER newPerfCounter;
|
||||
Tcl_WideInt nt0, nt1;
|
||||
|
||||
QueryPerformanceCounter(&newPerfCounter);
|
||||
nt0 = NativeCalc100NsTicks(timeInfo.fileTimeLastCall.QuadPart,
|
||||
timeInfo.perfCounterLastCall.QuadPart, timeInfo.curCounterFreq.QuadPart,
|
||||
newPerfCounter.QuadPart);
|
||||
nt1 = NativeCalc100NsTicks(vt0,
|
||||
curPerfCounter.QuadPart, estFreq,
|
||||
newPerfCounter.QuadPart);
|
||||
if (nt0 > nt1) { /* drifted backwards, try to compensate with new base */
|
||||
/* first adjust with a micro jump (short frozen time is acceptable) */
|
||||
vt0 += nt0 - nt1;
|
||||
/* if drift unavoidable (e. g. we had a time switch), then reset it */
|
||||
vt1 = vt0 - curFileTime.QuadPart;
|
||||
if (vt1 > 10000000 || vt1 < -10000000) {
|
||||
/* larger jump resp. shift relative new file-time */
|
||||
vt0 = curFileTime.QuadPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In lock commit new values to timeInfo (hold lock as short as possible) */
|
||||
EnterCriticalSection(&timeInfo.cs);
|
||||
|
||||
/* grow calibration interval up to 10 seconds (if still precise enough) */
|
||||
if (tdiff < -100000 || tdiff > 100000) {
|
||||
/* too long drift - reset calibration interval to 1000 second */
|
||||
timeInfo.calibrationInterv = 1;
|
||||
} else if (timeInfo.calibrationInterv < 10) {
|
||||
timeInfo.calibrationInterv++;
|
||||
}
|
||||
|
||||
timeInfo.fileTimeLastCall.QuadPart = vt0;
|
||||
timeInfo.curCounterFreq.QuadPart = estFreq;
|
||||
timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart;
|
||||
|
||||
LeaveCriticalSection(&timeInfo.cs);
|
||||
@@ -1079,7 +1355,11 @@ TclpGmtime(
|
||||
* Posix gmtime_r function.
|
||||
*/
|
||||
|
||||
#if defined(_WIN64) || defined(_USE_64BIT_TIME_T) || (defined(_MSC_VER) && _MSC_VER < 1400)
|
||||
return gmtime(timePtr);
|
||||
#else
|
||||
return _gmtime32((CONST __time32_t *)timePtr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1110,7 +1390,11 @@ TclpLocaltime(
|
||||
* provide a Posix localtime_r function.
|
||||
*/
|
||||
|
||||
#if defined(_WIN64) || defined(_USE_64BIT_TIME_T) || (defined(_MSC_VER) && _MSC_VER < 1400)
|
||||
return localtime(timePtr);
|
||||
#else
|
||||
return _localtime32((CONST __time32_t *)timePtr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user