diff options
author | sebres <sebres@users.sourceforge.net> | 2017-07-03 13:29:16 (GMT) |
---|---|---|
committer | sebres <sebres@users.sourceforge.net> | 2017-07-03 13:29:16 (GMT) |
commit | 4881937a78ead2f68f732520535e20097b33a4fc (patch) | |
tree | b7eb6037e8109a030dba8efb1fc54e06e85471c8 | |
parent | 68152b9280dd29d5a79dcbd75bd0b1e54998f141 (diff) | |
download | tcl-4881937a78ead2f68f732520535e20097b33a4fc.zip tcl-4881937a78ead2f68f732520535e20097b33a4fc.tar.gz tcl-4881937a78ead2f68f732520535e20097b33a4fc.tar.bz2 |
interim commit: time-drift backwards fix... (try to resolve using frequency factor)
-rw-r--r-- | win/tclWinTime.c | 308 |
1 files changed, 86 insertions, 222 deletions
diff --git a/win/tclWinTime.c b/win/tclWinTime.c index a30a952..c4206fe 100644 --- a/win/tclWinTime.c +++ b/win/tclWinTime.c @@ -17,12 +17,6 @@ #define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY) /* - * Number of samples over which to estimate the performance counter. - */ - -#define SAMPLES 64 - -/* * The following arrays contain the day of year for the last day of each * month, where index 1 is January. */ @@ -75,22 +69,15 @@ typedef struct TimeInfo { ULONGLONG fileTime; ULONGLONG virtTime; volatile /* used also to compare calibration epoch */ - LONGLONG perfCounter; + LONGLONG perfCounter; LONGLONG counterFreq; } lastCC; /* Last data updated in calibration cycle */ + int freqFactor; /* Frequency factor (1000 - KHz, 1 - Hz) */ + Tcl_WideInt lastUsedTime; /* Last known (caller) virtual time in 100-ns * (used to avoid drifts after calibrate) */ - /* - * Data used in developing the estimate of performance counter frequency - */ - - Tcl_WideUInt fileTimeSample[SAMPLES]; - /* Last 64 samples of system time. */ - Tcl_WideInt perfCounterSample[SAMPLES]; - /* Last 64 samples of performance counter. */ - int sampleNo; /* Current sample number. */ } TimeInfo; static TimeInfo timeInfo = { @@ -114,10 +101,8 @@ static TimeInfo timeInfo = { (LONGLONG) 0, (LONGLONG) 0 }, - (Tcl_WideInt) 0, - { (Tcl_WideUInt) 0 }, - { (Tcl_WideInt) 0 }, - 0 + 1000, /* KHz */ + (Tcl_WideInt) 0 }; /* @@ -139,10 +124,6 @@ static struct tm * ComputeGMT(const time_t *tp); static void StopCalibration(ClientData clientData); static DWORD WINAPI CalibrationThread(LPVOID arg); static void UpdateTimeEachSecond(void); -static void ResetCounterSamples(Tcl_WideUInt fileTime, - Tcl_WideInt perfCounter, Tcl_WideInt perfFreq); -static Tcl_WideInt AccumulateSample(Tcl_WideInt perfCounter, - Tcl_WideUInt fileTime); static void NativeScaleTime(Tcl_Time* timebuf, ClientData clientData); static Tcl_WideInt NativeGetMicroseconds(void); @@ -658,6 +639,12 @@ NativeGetMicroseconds(void) if (timeInfo.perfCounterAvailable) { DWORD id; + /* Some systems having frequency in Hz, so save the factor here */ + if (timeInfo.nominalFreq.QuadPart >= 1000000000) { + /* assume that frequency in Hz, factor used only for tolerance */ + timeInfo.freqFactor = 1; + } + InitializeCriticalSection(&timeInfo.cs); timeInfo.readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); timeInfo.exitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -1194,10 +1181,6 @@ CalibrationThread( timeInfo.lastCC.counterFreq = timeInfo.nominalFreq.QuadPart; timeInfo.lastCC.fileTime = timeInfo.lastCC.virtTime = GetSystemTimeAsVirtual(); - ResetCounterSamples(timeInfo.lastCC.fileTime, - timeInfo.lastCC.perfCounter, - timeInfo.lastCC.counterFreq); - /* * Calibrate first time and wake up the calling thread. * When it wakes up, it will release the initialization lock. @@ -1260,9 +1243,6 @@ UpdateTimeEachSecond(void) LARGE_INTEGER curPerfCounter; /* Current value returned from * QueryPerformanceCounter. */ - static Tcl_WideInt lastDiff = 0; - /* Last difference between system clock and Tcl - * time. */ static int calibrationInterv = 10000000; /* Calibration interval in 100-ns ticks (starts from 1s) */ Tcl_WideInt curFileTime; /* File time at the time this callback was @@ -1271,27 +1251,30 @@ UpdateTimeEachSecond(void) int driftBack; /* Sign the virtual time may drift backwards */ Tcl_WideInt vt0; /* Tcl time right now. */ Tcl_WideInt vt1; /* Interim virtual time used during adjustments */ - Tcl_WideInt tdiff; /* Difference between system clock and Tcl - * time. */ + Tcl_WideInt tdiff, /* Difference between system clock and Tcl time. */ + lastDiff; /* Difference of last calibration. */ - printf("-------------calibration start, prev-struct: %I64d, %I64d, %I64d\n", timeInfo.lastCC.fileTime, timeInfo.lastCC.perfCounter, timeInfo.lastCC.counterFreq); /* * Sample system time (from posix epoch) and performance counter. */ curFileTime = GetSystemTimeAsVirtual(); QueryPerformanceCounter(&curPerfCounter); + printf("-------------calibration start, prev-struct: %I64d, %I64d, %I64d, pc-diff: %I64d\n", timeInfo.lastCC.fileTime, timeInfo.lastCC.perfCounter, timeInfo.lastCC.counterFreq, curPerfCounter.QuadPart - timeInfo.lastCC.perfCounter); /* - * Current virtual time: - * vt0 = lastCC.fileTime + + * Current virtual time (using average between fileTime and virtTime): + * vt0 = (lastCC.fileTime + lastCC.virtTime) / 2 + * 10000000 * (curPerfCounter - lastCC.perfCounter) / lastCC.counterFreq */ - vt0 = NativeCalc100NsTicks((timeInfo.lastCC.fileTime/2 + timeInfo.lastCC.virtTime/2), + vt0 = NativeCalc100NsTicks( + (timeInfo.lastCC.fileTime/2 + timeInfo.lastCC.virtTime/2), timeInfo.lastCC.perfCounter, timeInfo.lastCC.counterFreq, curPerfCounter.QuadPart); - tdiff = vt0 - curFileTime; /* discrepancy between virtual and real-time */ + /* Differences between virtual and real-time */ + tdiff = vt0 - curFileTime; + lastDiff = timeInfo.lastCC.virtTime - timeInfo.lastCC.fileTime; if (tdiff >= 10000000 || tdiff <= -10000000) { printf("---!!!!!!!---calibration ERR, tdiff %I64d\n", tdiff); } @@ -1300,14 +1283,13 @@ UpdateTimeEachSecond(void) * NativeGetMicroseconds checks calibNextTime also, be sure it does not overflow. * Calibrate immediately if we've too large discrepancy to the real-time (15.6 ms). */ -#if 0 +#if 1 if ( curFileTime < timeInfo.calibNextTime - (10000000/2) /* 0.5 sec (in 100-ns ticks). */ && timeInfo.calibNextTime - curFileTime < 10 * 10000000 /* max. 10 seconds in-between (time-switch?) */ - && tdiff > -156000 && tdiff < 156000 /* small discrepancy */ - && (tdiff >= 0 && tdiff <= lastDiff / 3 * 2 || tdiff < 0 && tdiff >= lastDiff / 3 * 2) /* more precise now */ + && tdiff > -10000 && tdiff < 10000 /* very small discrepancy (1ms) */ ) { /* again in next one second */ - printf("-------------calibration end, tdiff %I64d, *** not needed. (next in: %I64d) ------\n", tdiff, curFileTime, timeInfo.calibNextTime, timeInfo.calibNextTime - curFileTime); + //printf("-------------calibration end, tdiff %I64d, *** not needed. (next in: %I64d) ------\n", tdiff, curFileTime, timeInfo.calibNextTime, timeInfo.calibNextTime - curFileTime); lastDiff = tdiff; return; } @@ -1330,39 +1312,7 @@ UpdateTimeEachSecond(void) * Several things may have gone wrong here that have to be checked for. * (1) The performance counter may have jumped. * (2) The system clock may have been reset. - * - * In either case, we'll need to reinitialize the circular buffer with - * samples relative to the current system time and the NOMINAL performance - * frequency (not the actual, because the actual has probably run slow in - * the first case). Our estimated frequency will be the nominal frequency. - * - * Store the current sample into the circular buffer of samples, and - * estimate the performance counter frequency. */ -#if 0 - estFreq = AccumulateSample(curPerfCounter.QuadPart, curFileTime); -#else - vt1 = curFileTime - timeInfo.lastCC.fileTime; - if (vt1) { - estFreq = 10000000 * (curPerfCounter.QuadPart - timeInfo.lastCC.perfCounter) / vt1; -#if 1 - - /* - * Minimize influence of estFreq if tdiff falls (in relation to last difference), - * with dual falling speed. This indicates better choice of lastCC.counterFreq. - */ - if (tdiff > 0 && tdiff < lastDiff / 2 || tdiff < 0 && tdiff > lastDiff / 2) { - printf("-----***-----calibration minimize %I64d, %I64d\n", estFreq, lastDiff); - estFreq = (estFreq + timeInfo.lastCC.counterFreq * 2) / 3; - printf("-----***-----calibration minimize %I64d, %I64d\n", estFreq, tdiff); - } -#endif - } else { - estFreq = timeInfo.lastCC.counterFreq; - } - //printf("------**-----calibration estimated, tdiff: %I64d, ** %s ** cntrDiff:%I64d\n", tdiff, (estFreq > timeInfo.lastCC.counterFreq) ? "^^^" : "vvv", (curPerfCounter.QuadPart - timeInfo.lastCC.perfCounter)); - //printf("------**-----calibration estimated Frequency %I64d, %I64d, %I64d, diff: %I64d\n", curFileTime, curPerfCounter.QuadPart, estFreq, estFreq - timeInfo.lastCC.counterFreq); -#endif driftBack = 0; @@ -1373,10 +1323,10 @@ UpdateTimeEachSecond(void) * Ideally, we would like to drift the clock into place over a period of 2 * sec, so that virtual time 2 sec from now will be * - * vt1 = 20000000 + curFileTime + * vt1 = 10000000 + curFileTime * * The frequency that we need to use to drift the counter back into place - * is estFreq * 20000000 / (vt1 - vt0) + * is estFreq * 10000000 / (vt1 - vt0) * * 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, @@ -1384,18 +1334,47 @@ UpdateTimeEachSecond(void) */ if (tdiff > 10000000 || tdiff < -10000000) { - /* More as a second difference, so could be a time-switch + /* More as a second difference, so could be a time-switch (reset) /* jump to current system time, use curent estimated frequency */ vt0 = curFileTime; timeInfo.lastUsedTime = 0; /* reset last used time */ estFreq = timeInfo.nominalFreq.QuadPart; } else { + Tcl_WideInt driftFreq; /* Frequency needed to drift virtual time into * step over 1 second. */ - /* calculate new frequency and estimate drift to the next second */ - vt1 = 20000000 + curFileTime; - driftFreq = (estFreq * 20000000 / (vt1 - vt0)); + /* + * Estimate current frequency corresponding current time / counter. + */ + + vt1 = curFileTime - timeInfo.lastCC.fileTime; + if (vt1 > 0) { + estFreq = (curPerfCounter.QuadPart - timeInfo.lastCC.perfCounter) * 10000000 / vt1; + + /* + * Minimize influence of estFreq if tdiff falls (in relation to + * last difference), with dual falling speed. This indicates better + * choice of lastCC.counterFreq. + */ + if (tdiff > 0 && tdiff < lastDiff / 2 || tdiff < 0 && tdiff > lastDiff / 2) { + //printf("-----***-----calibration minimize %I64d, %I64d\n", estFreq, lastDiff); + estFreq = (estFreq + timeInfo.lastCC.counterFreq * 2) / 3; + //printf("-----***-----calibration minimize %I64d, %I64d\n", estFreq, tdiff); + } + } else { + estFreq = timeInfo.lastCC.counterFreq; + } + printf("------**-----calibration estimated, tdiff: %I64d, ** %s ** cntrDiff:%I64d\n", tdiff, (estFreq > timeInfo.lastCC.counterFreq) ? "^^^" : "vvv", (curPerfCounter.QuadPart - timeInfo.lastCC.perfCounter)); + printf("------**-----calibration estimated Frequency %I64d, %I64d, %I64d, diff: %I64d, to-nomin: %I64d\n", curFileTime, curPerfCounter.QuadPart, estFreq, estFreq - timeInfo.lastCC.counterFreq, estFreq - timeInfo.nominalFreq.QuadPart); + +#if 1 + /* + * Calculate new frequency and estimate drift to the next second + */ + + vt1 = 10000000 + curFileTime; + driftFreq = estFreq * 10000000 / (vt1 - vt0); /* * Avoid too large drifts (only half of the current difference), * that allows also be more accurate (aspire to the smallest tdiff), @@ -1404,6 +1383,10 @@ UpdateTimeEachSecond(void) driftFreq = timeInfo.lastCC.counterFreq + (driftFreq - timeInfo.lastCC.counterFreq) / 2; + printf("------**-----calibration lastFreq: %I64d\n", timeInfo.lastCC.counterFreq); + printf("------**-----calibration estFreq: %I64d\n", estFreq); + printf("------**-----calibration driftFreq:%I64d\n", driftFreq); + /* * Average between estimated, 2 current and 5 drifted frequencies, * (do the soft drifting as possible). @@ -1418,6 +1401,10 @@ UpdateTimeEachSecond(void) #else estFreq = (estFreq + timeInfo.lastCC.counterFreq + driftFreq) / 3; #endif + +#else + estFreq = (estFreq + timeInfo.lastCC.counterFreq) / 2; +#endif } /* @@ -1426,14 +1413,22 @@ UpdateTimeEachSecond(void) if ( estFreq > (vt1 = (1000+5)*timeInfo.nominalFreq.QuadPart/1000) || estFreq < (vt1 = (1000-5)*timeInfo.nominalFreq.QuadPart/1000) ) { - estFreq = vt1; - driftBack = vt0 > curFileTime; - vt0 = curFileTime; /* too large - just reset */ + /* Some systems having frequency in Hz, so fewer tolerant (1.5%) */ + if ( timeInfo.freqFactor == 1000 /* frequency in KHz */ + || ( estFreq > (vt1 = (1000+15)*timeInfo.nominalFreq.QuadPart/1000) + || estFreq < (vt1 = (1000-15)*timeInfo.nominalFreq.QuadPart/1000) + ) + ) { + estFreq = vt1; + driftBack = vt0 > curFileTime; + vt0 = curFileTime; /* too large - just reset */ + printf("************ too large: %I64d\n", estFreq); + } } /* If possible backwards time-drifts (larger divider now) */ vt1 = 0; - if (1 || driftBack || estFreq > timeInfo.lastCC.counterFreq) { + if (driftBack || estFreq > timeInfo.lastCC.counterFreq) { Tcl_WideInt nt0, nt1; /* @@ -1463,15 +1458,14 @@ UpdateTimeEachSecond(void) } /* if still precise enough, grow calibration interval up to 10 seconds */ - if (tdiff < -156000 || tdiff > 156000 /* 15.6-ms */) { - /* too long drift - reset calibration interval to 1 second */ - calibrationInterv = 10000000; - } else if (calibrationInterv < 10*10000000) { - calibrationInterv += 10000000; + if ( timeInfo.freqFactor == 1000 ) { /* frequency in KHz */ + if (tdiff < -100000 || tdiff > 100000 /* 10-ms */) { + /* too long drift - reset calibration interval to 1 second */ + calibrationInterv = 10000000; + } else if (calibrationInterv < 10*10000000) { + calibrationInterv += 10000000; + } } - - lastDiff = tdiff; - /* In lock commit new values to timeInfo (hold lock as short as possible) */ EnterCriticalSection(&timeInfo.cs); @@ -1494,136 +1488,6 @@ UpdateTimeEachSecond(void) /* *---------------------------------------------------------------------- * - * ResetCounterSamples -- - * - * Fills the sample arrays in 'timeInfo' with dummy values that will - * yield the current performance counter and frequency. - * - * Results: - * None. - * - * Side effects: - * The array of samples is filled in so that it appears that there are - * SAMPLES samples at one-second intervals, separated by precisely the - * given frequency. - * - *---------------------------------------------------------------------- - */ - -static void -ResetCounterSamples( - Tcl_WideUInt fileTime, /* Current file time */ - Tcl_WideInt perfCounter, /* Current performance counter */ - Tcl_WideInt perfFreq) /* Target performance frequency */ -{ - int i; - for (i=SAMPLES-1 ; i>=0 ; --i) { - timeInfo.perfCounterSample[i] = perfCounter; - timeInfo.fileTimeSample[i] = fileTime; - perfCounter -= perfFreq; - fileTime -= 10000000; - } - timeInfo.sampleNo = 0; -} - -/* - *---------------------------------------------------------------------- - * - * AccumulateSample -- - * - * Updates the circular buffer of performance counter and system time - * samples with a new data point. - * - * Results: - * None. - * - * Side effects: - * The new data point replaces the oldest point in the circular buffer, - * and the descriptive statistics are updated to accumulate the new - * point. - * - * Several things may have gone wrong here that have to be checked for. - * (1) The performance counter may have jumped. - * (2) The system clock may have been reset. - * - * In either case, we'll need to reinitialize the circular buffer with samples - * relative to the current system time and the NOMINAL performance frequency - * (not the actual, because the actual has probably run slow in the first - * case). - */ - -static Tcl_WideInt -AccumulateSample( - Tcl_WideInt perfCounter, - Tcl_WideUInt fileTime) -{ - Tcl_WideUInt workFTSample; /* File time sample being removed from or - * added to the circular buffer. */ - Tcl_WideInt workPCSample; /* Performance counter sample being removed - * from or added to the circular buffer. */ - Tcl_WideUInt lastFTSample; /* Last file time sample recorded */ - Tcl_WideInt lastPCSample; /* Last performance counter sample recorded */ - Tcl_WideInt FTdiff; /* Difference between last FT and current */ - Tcl_WideInt PCdiff; /* Difference between last PC and current */ - Tcl_WideInt estFreq; /* Estimated performance counter frequency */ - - /* - * Test for jumps and reset the samples if we have one. - */ - - if (timeInfo.sampleNo == 0) { - lastPCSample = - timeInfo.perfCounterSample[timeInfo.sampleNo + SAMPLES - 1]; - lastFTSample = - timeInfo.fileTimeSample[timeInfo.sampleNo + SAMPLES - 1]; - } else { - lastPCSample = timeInfo.perfCounterSample[timeInfo.sampleNo - 1]; - lastFTSample = timeInfo.fileTimeSample[timeInfo.sampleNo - 1]; - } - - PCdiff = perfCounter - lastPCSample; - FTdiff = fileTime - lastFTSample; - if (PCdiff < timeInfo.nominalFreq.QuadPart * 9 / 10 - || PCdiff > timeInfo.nominalFreq.QuadPart * 11 / 10 - || FTdiff < 9000000 || FTdiff > 11000000) { - ResetCounterSamples(fileTime, perfCounter, - timeInfo.nominalFreq.QuadPart); -#if 0 - FTdiff = fileTime - timeInfo.lastCC.fileTime; - if (FTdiff) { - estFreq = 10000000 * (perfCounter - timeInfo.lastCC.perfCounter) / FTdiff; - printf("------**-----calibration estimated Frequency %I64d, %I64d, %I64d, diff: %I64d\n", fileTime, perfCounter, estFreq, estFreq - timeInfo.lastCC.counterFreq); - } - return (timeInfo.nominalFreq.QuadPart + timeInfo.lastCC.counterFreq) / 2; -#endif - return timeInfo.nominalFreq.QuadPart; - } else { - /* - * Estimate the frequency. - */ - - workPCSample = timeInfo.perfCounterSample[timeInfo.sampleNo]; - workFTSample = timeInfo.fileTimeSample[timeInfo.sampleNo]; - estFreq = 10000000 * (perfCounter - workPCSample) - / (fileTime - workFTSample); - timeInfo.perfCounterSample[timeInfo.sampleNo] = perfCounter; - timeInfo.fileTimeSample[timeInfo.sampleNo] = (Tcl_WideInt) fileTime; - - /* - * Advance the sample number. - */ - - if (++timeInfo.sampleNo >= SAMPLES) { - timeInfo.sampleNo = 0; - } - - return estFreq; - } -} - -/* - *---------------------------------------------------------------------- - * * TclpGmtime -- * * Wrapper around the 'gmtime' library function to make it thread safe. |