Attachment "None" to
ticket [402459ffff]
added by
kennykb
2000-11-21 05:34:21.
*** ../tcl8.3.2base/src/tcl8.3.2/win/tclWinNotify.c Fri Jul 2 18:08:30 1999
--- ./src/tcl8.3.2/win/tclWinNotify.c Thu Aug 24 23:29:12 2000
***************
*** 510,514 ****
Tcl_Sleep(ms)
int ms; /* Number of milliseconds to sleep. */
{
! Sleep(ms);
}
--- 510,548 ----
Tcl_Sleep(ms)
int ms; /* Number of milliseconds to sleep. */
{
! /*
! * Simply calling 'Sleep' for the requisite number of milliseconds
! * can make the process appear to wake up early because it isn't
! * synchronized with the CPU performance counter that is used in
! * tclWinTime.c. This behavior is probably benign, but messes
! * up some of the corner cases in the test suite. We get around
! * this problem by repeating the 'Sleep' call as many times
! * as necessary to make the clock advance by the requisite amount.
! */
!
! Tcl_Time now; /* Current wall clock time */
! Tcl_Time desired; /* Desired wakeup time */
! int sleepTime = ms; /* Time to sleep */
!
! TclpGetTime( &now );
! desired.sec = now.sec + ( ms / 1000 );
! desired.usec = now.usec + 1000 * ( ms % 1000 );
! if ( desired.usec > 1000000 ) {
! ++desired.sec;
! desired.usec -= 1000000;
! }
!
! for ( ; ; ) {
! Sleep( sleepTime );
! TclpGetTime( &now );
! if ( now.sec > desired.sec ) {
! break;
! } else if ( ( now.sec == desired.sec )
! && ( now.usec >= desired.usec ) ) {
! break;
! }
! sleepTime = ( ( 1000 * ( desired.sec - now.sec ) )
! + ( ( desired.usec - now.usec ) / 1000 ) );
! }
!
}
*** ../tcl8.3.2base/src/tcl8.3.2/win/tclWinTest.c Thu Oct 28 23:05:14 1999
--- ./src/tcl8.3.2/win/tclWinTest.c Mon Sep 4 22:45:56 2000
***************
*** 22,27 ****
--- 22,31 ----
static int TestvolumetypeCmd _ANSI_ARGS_((ClientData dummy,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
+ static int TestwinclockCmd _ANSI_ARGS_(( ClientData dummy,
+ Tcl_Interp* interp,
+ int objc,
+ Tcl_Obj *CONST objv[] ));
/*
*----------------------------------------------------------------------
***************
*** 52,57 ****
--- 56,63 ----
(ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "testvolumetype", TestvolumetypeCmd,
(ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateObjCommand(interp, "testwinclock", TestwinclockCmd,
+ (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}
***************
*** 187,190 ****
--- 193,267 ----
Tcl_SetResult(interp, volType, TCL_VOLATILE);
return TCL_OK;
#undef VOL_BUF_SIZE
+ }
+
+ /*
+ *----------------------------------------------------------------------
+ *
+ * TestclockCmd --
+ *
+ * Command that returns the seconds and microseconds portions of
+ * the system clock and of the Tcl clock so that they can be
+ * compared to validate that the Tcl clock is staying in sync.
+ *
+ * Usage:
+ * testclock
+ *
+ * Parameters:
+ * None.
+ *
+ * Results:
+ * Returns a standard Tcl result comprising a four-element list:
+ * the seconds and microseconds portions of the system clock,
+ * and the seconds and microseconds portions of the Tcl clock.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ static int
+ TestwinclockCmd( ClientData dummy,
+ /* Unused */
+ Tcl_Interp* interp,
+ /* Tcl interpreter */
+ int objc,
+ /* Argument count */
+ Tcl_Obj *CONST objv[] )
+ /* Argument vector */
+ {
+ CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
+ /* The Posix epoch, expressed as a
+ * Windows FILETIME */
+ Tcl_Time tclTime; /* Tcl clock */
+ FILETIME sysTime; /* System clock */
+ Tcl_Obj* result; /* Result of the command */
+ LARGE_INTEGER t1, t2;
+
+ if ( objc != 1 ) {
+ Tcl_WrongNumArgs( interp, 1, objv, "" );
+ return TCL_ERROR;
+ }
+
+ TclpGetTime( &tclTime );
+ GetSystemTimeAsFileTime( &sysTime );
+ t1.LowPart = posixEpoch.dwLowDateTime;
+ t1.HighPart = posixEpoch.dwHighDateTime;
+ t2.LowPart = sysTime.dwLowDateTime;
+ t2.HighPart = sysTime.dwHighDateTime;
+ t2.QuadPart -= t1.QuadPart;
+
+ result = Tcl_NewObj();
+ Tcl_ListObjAppendElement
+ ( interp, result, Tcl_NewIntObj( (int) (t2.QuadPart / 10000000 ) ) );
+ Tcl_ListObjAppendElement
+ ( interp, result,
+ Tcl_NewIntObj( (int) ( (t2.QuadPart / 10 ) % 1000000 ) ) );
+ Tcl_ListObjAppendElement( interp, result, Tcl_NewIntObj( tclTime.sec ) );
+ Tcl_ListObjAppendElement( interp, result, Tcl_NewIntObj( tclTime.usec ) );
+
+ Tcl_SetObjResult( interp, result );
+
+ return TCL_OK;
}
*** ../tcl8.3.2base/src/tcl8.3.2/win/tclWinTime.c Tue Nov 30 19:08:44 1999
--- ./src/tcl8.3.2/win/tclWinTime.c Thu Nov 2 14:25:56 2000
***************
*** 38,47 ****
--- 38,114 ----
static Tcl_ThreadDataKey dataKey;
/*
+ * Calibration interval for the high-resolution timer, in msec
+ */
+
+ static CONST unsigned long clockCalibrateWakeupInterval = 10000;
+ /* FIXME: 10 s -- should be about 10 min! */
+
+ /*
+ * Data for managing high-resolution timers.
+ */
+
+ typedef struct TimeInfo {
+
+ CRITICAL_SECTION cs; /* Mutex guarding this structure */
+
+ int initialized; /* Flag == 1 if this structure is
+ * initialized. */
+
+ int perfCounterAvailable; /* Flag == 1 if the hardware has a
+ * performance counter */
+
+ HANDLE calibrationThread; /* Handle to the thread that keeps the
+ * virtual clock calibrated. */
+
+ HANDLE readyEvent; /* System event used to
+ * trigger the requesting thread
+ * when the clock calibration procedure
+ * is initialized for the first time */
+
+ /*
+ * The following values are used for calculating virtual time.
+ * Virtual time is always equal to:
+ * lastFileTime + (current perf counter - lastCounter)
+ * * 10000000 / curCounterFreq
+ * and lastFileTime and lastCounter are updated any time that
+ * virtual time is returned to a caller.
+ */
+
+ ULARGE_INTEGER lastFileTime;
+ LARGE_INTEGER lastCounter;
+ LARGE_INTEGER curCounterFreq;
+
+ /*
+ * The next two values are used only in the calibration thread, to track
+ * the frequency of the performance counter.
+ */
+
+ LONGLONG lastPerfCounter; /* Performance counter the last time
+ * that UpdateClockEachSecond was called */
+ LONGLONG lastSysTime; /* System clock at the last time
+ * that UpdateClockEachSecond was called */
+ LONGLONG estPerfCounterFreq;
+ /* Current estimate of the counter frequency
+ * using the system clock as the standard */
+
+ } TimeInfo;
+
+ static TimeInfo timeInfo = {
+ NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0
+ };
+
+ CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
+
+ /*
* Declarations for functions defined later in this file.
*/
static struct tm * ComputeGMT _ANSI_ARGS_((const time_t *tp));
+
+ static DWORD WINAPI CalibrationThread _ANSI_ARGS_(( LPVOID arg ));
+
+ static void UpdateTimeEachSecond _ANSI_ARGS_(( void ));
/*
*----------------------------------------------------------------------
***************
*** 63,69 ****
unsigned long
TclpGetSeconds()
{
! return (unsigned long) time((time_t *) NULL);
}
/*
--- 130,138 ----
unsigned long
TclpGetSeconds()
{
! Tcl_Time t;
! TclpGetTime( &t );
! return t.sec;
}
/*
***************
*** 89,95 ****
unsigned long
TclpGetClicks()
{
! return GetTickCount();
}
/*
--- 158,175 ----
unsigned long
TclpGetClicks()
{
! /*
! * Use the TclpGetTime abstraction to get the time in microseconds,
! * as nearly as we can, and return it.
! */
!
! Tcl_Time now; /* Current Tcl time */
! unsigned long retval; /* Value to return */
!
! TclpGetTime( &now );
! retval = ( now.sec * 1000000 ) + now.usec;
! return retval;
!
}
/*
***************
*** 134,140 ****
* Returns the current time in timePtr.
*
* Side effects:
! * None.
*
*----------------------------------------------------------------------
*/
--- 214,226 ----
* Returns the current time in timePtr.
*
* Side effects:
! * On the first call, initializes a set of static variables to
! * keep track of the base value of the performance counter, the
! * corresponding wall clock (obtained through ftime) and the
! * frequency of the performance counter. Also spins a thread
! * whose function is to wake up periodically and monitor these
! * values, adjusting them as necessary to correct for drift
! * in the performance counter's oscillator.
*
*----------------------------------------------------------------------
*/
***************
*** 143,153 ****
TclpGetTime(timePtr)
Tcl_Time *timePtr; /* Location to store time information. */
{
struct timeb t;
! ftime(&t);
! timePtr->sec = t.time;
! timePtr->usec = t.millitm * 1000;
}
/*
--- 229,342 ----
TclpGetTime(timePtr)
Tcl_Time *timePtr; /* Location to store time information. */
{
+
struct timeb t;
! /* Initialize static storage on the first trip through. */
!
! /*
! * Note: Outer check for 'initialized' is a performance win
! * since it avoids an extra mutex lock in the common case.
! */
!
! if ( !timeInfo.initialized ) {
! TclpInitLock();
! if ( !timeInfo.initialized ) {
! timeInfo.perfCounterAvailable
! = QueryPerformanceFrequency( &timeInfo.curCounterFreq );
!
! /*
! * Some hardware abstraction layers use the CPU clock
! * in place of the real-time clock as a performance counter
! * reference. This results in:
! * - inconsistent results among the processors on
! * multi-processor systems.
! * - unpredictable changes in performance counter frequency
! * on "gearshift" processors such as Transmeta and
! * SpeedStep.
! * There seems to be no way to test whether the performance
! * counter is reliable, but a useful heuristic is that
! * if its frequency is 1.193182 MHz or 3.579545 MHz, it's
! * derived from a colorburst crystal and is therefore
! * the RTC rather than the TSC. If it's anything else, we
! * presume that the performance counter is unreliable.
! */
!
! if ( timeInfo.perfCounterAvailable
! && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 1193182
! && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 3579545 ) {
! timeInfo.perfCounterAvailable = FALSE;
! }
!
! /*
! * If the performance counter is available, start a thread to
! * calibrate it.
! */
!
! if ( timeInfo.perfCounterAvailable ) {
! DWORD id;
! InitializeCriticalSection( &timeInfo.cs );
! timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
! timeInfo.calibrationThread = CreateThread( NULL,
! 8192,
! CalibrationThread,
! (LPVOID) NULL,
! 0,
! &id );
! SetThreadPriority( timeInfo.calibrationThread,
! THREAD_PRIORITY_HIGHEST );
! WaitForSingleObject( timeInfo.readyEvent, INFINITE );
! CloseHandle( timeInfo.readyEvent );
! }
! timeInfo.initialized = TRUE;
! }
! TclpInitUnlock();
! }
!
! if ( timeInfo.perfCounterAvailable ) {
!
! /*
! * Query the performance counter and use it to calculate the
! * current time.
! */
!
! LARGE_INTEGER curCounter;
! /* Current performance counter */
!
! LONGLONG curFileTime;
! /* Current estimated time, expressed
! * as 100-ns ticks since the Windows epoch */
!
! static const LARGE_INTEGER posixEpoch = { 0xD53E8000, 0x019DB1DE };
! /* Posix epoch expressed as 100-ns ticks
! * since the windows epoch */
!
! LONGLONG usecSincePosixEpoch;
! /* Current microseconds since Posix epoch */
!
! EnterCriticalSection( &timeInfo.cs );
!
! QueryPerformanceCounter( &curCounter );
! curFileTime = timeInfo.lastFileTime.QuadPart
! + ( ( curCounter.QuadPart - timeInfo.lastCounter.QuadPart )
! * 10000000 / timeInfo.curCounterFreq.QuadPart );
! timeInfo.lastFileTime.QuadPart = curFileTime;
! timeInfo.lastCounter.QuadPart = curCounter.QuadPart;
! usecSincePosixEpoch = ( curFileTime - posixEpoch.QuadPart ) / 10;
! timePtr->sec = (time_t) ( usecSincePosixEpoch / 1000000 );
! timePtr->usec = (unsigned long ) ( usecSincePosixEpoch % 1000000 );
!
! LeaveCriticalSection( &timeInfo.cs );
!
!
! } else {
!
! /* High resolution timer is not available. Just use ftime */
!
! ftime(&t);
! timePtr->sec = t.time;
! timePtr->usec = t.millitm * 1000;
! }
}
/*
***************
*** 439,442 ****
--- 628,843 ----
}
return tmPtr;
+ }
+
+ /*
+ *----------------------------------------------------------------------
+ *
+ * CalibrationThread --
+ *
+ * Thread that manages calibration of the hi-resolution time
+ * derived from the performance counter, to keep it synchronized
+ * with the system clock.
+ *
+ * Parameters:
+ * arg -- Client data from the CreateThread call. This parameter
+ * points to the static TimeInfo structure.
+ *
+ * Return value:
+ * None. This thread embeds an infinite loop.
+ *
+ * Side effects:
+ * At an interval of clockCalibrateWakeupInterval ms, this thread
+ * performs virtual time discipline.
+ *
+ * Note: When this thread is entered, TclpInitLock has been called
+ * to safeguard the static storage. There is therefore no synchronization
+ * in the body of this procedure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ static DWORD WINAPI
+ CalibrationThread( LPVOID arg )
+ {
+ FILETIME curFileTime;
+
+ /* Get initial system time and performance counter */
+
+ GetSystemTimeAsFileTime( &curFileTime );
+ QueryPerformanceCounter( &timeInfo.lastCounter );
+ QueryPerformanceFrequency( &timeInfo.curCounterFreq );
+ timeInfo.lastFileTime.LowPart = curFileTime.dwLowDateTime;
+ timeInfo.lastFileTime.HighPart = curFileTime.dwHighDateTime;
+
+ /* Initialize the working storage for the calibration callback */
+
+ timeInfo.lastPerfCounter = timeInfo.lastCounter.QuadPart;
+ timeInfo.estPerfCounterFreq = timeInfo.curCounterFreq.QuadPart;
+
+ /*
+ * Wake up the calling thread. When it wakes up, it will release the
+ * initialization lock.
+ */
+
+ SetEvent( timeInfo.readyEvent );
+
+ /* Run the calibration once a second */
+
+ for ( ; ; ) {
+
+ Sleep( 1000 );
+ UpdateTimeEachSecond();
+
+ }
+ }
+
+ /*
+ *----------------------------------------------------------------------
+ *
+ * UpdateTimeEachSecond --
+ *
+ * Callback from the waitable timer in the clock calibration thread
+ * that updates system time.
+ *
+ * Parameters:
+ * info -- Pointer to the static TimeInfo structure
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Performs virtual time calibration discipline.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ static void
+ UpdateTimeEachSecond()
+ {
+
+ LARGE_INTEGER curPerfCounter;
+ /* Current value returned from
+ * QueryPerformanceCounter */
+
+ LONGLONG perfCounterDiff; /* Difference between the current value
+ * and the value of 1 second ago */
+
+ FILETIME curSysTime; /* Current system time */
+
+ LARGE_INTEGER curFileTime; /* File time at the time this callback
+ * was scheduled. */
+
+ LONGLONG fileTimeDiff; /* Elapsed time on the system clock
+ * since the last time this procedure
+ * was called */
+
+ LONGLONG instantFreq; /* Instantaneous estimate of the
+ * performance counter frequency */
+
+ LONGLONG delta; /* Increment to add to the estimated
+ * performance counter frequency in the
+ * loop filter */
+
+ LONGLONG fuzz; /* Tolerance for the perf counter frequency */
+
+ LONGLONG lowBound; /* Lower bound for the frequency assuming
+ * 1000 ppm tolerance */
+
+ LONGLONG hiBound; /* Upper bound for the frequency */
+
+ /*
+ * Get current performance counter and system time.
+ */
+
+ QueryPerformanceCounter( &curPerfCounter );
+ GetSystemTimeAsFileTime( &curSysTime );
+ curFileTime.LowPart = curSysTime.dwLowDateTime;
+ curFileTime.HighPart = curSysTime.dwHighDateTime;
+
+ EnterCriticalSection( &timeInfo.cs );
+
+ /*
+ * Find out how many ticks of the performance counter and the
+ * system clock have elapsed since we got into this procedure.
+ * Estimate the current frequency.
+ */
+
+ perfCounterDiff = curPerfCounter.QuadPart - timeInfo.lastPerfCounter;
+ timeInfo.lastPerfCounter = curPerfCounter.QuadPart;
+ fileTimeDiff = curFileTime.QuadPart - timeInfo.lastSysTime;
+ timeInfo.lastSysTime = curFileTime.QuadPart;
+ instantFreq = ( 10000000 * perfCounterDiff / fileTimeDiff );
+
+ /*
+ * Consider this a timing glitch if instant frequency varies
+ * significantly from the current estimate.
+ */
+
+ fuzz = timeInfo.estPerfCounterFreq >> 10;
+ lowBound = timeInfo.estPerfCounterFreq - fuzz;
+ hiBound = timeInfo.estPerfCounterFreq + fuzz;
+ if ( instantFreq < lowBound || instantFreq > hiBound ) {
+ LeaveCriticalSection( &timeInfo.cs );
+ return;
+ }
+
+ /*
+ * Update the current estimate of performance counter frequency.
+ * This code is equivalent to the loop filter of a phase locked
+ * loop.
+ */
+
+ delta = ( instantFreq - timeInfo.estPerfCounterFreq ) >> 6;
+ timeInfo.estPerfCounterFreq += delta;
+
+ /*
+ * Update the current virtual time.
+ */
+
+ timeInfo.lastFileTime.QuadPart
+ += ( ( curPerfCounter.QuadPart - timeInfo.lastCounter.QuadPart )
+ * 10000000 / timeInfo.curCounterFreq.QuadPart );
+ timeInfo.lastCounter.QuadPart = curPerfCounter.QuadPart;
+
+ delta = curFileTime.QuadPart - timeInfo.lastFileTime.QuadPart;
+ if ( delta > 10000000 || delta < -10000000 ) {
+
+ /*
+ * If the virtual time slip exceeds one second, then adjusting
+ * the counter frequency is hopeless (it'll take over fifteen
+ * minutes to line up with the system clock). The most likely
+ * cause of this large a slip is a sudden change to the system
+ * clock, perhaps because it was being corrected by wristwatch
+ * and eyeball. Accept the system time, and set the performance
+ * counter frequency to the current estimate.
+ */
+
+ timeInfo.lastFileTime.QuadPart = curFileTime.QuadPart;
+ timeInfo.curCounterFreq.QuadPart = timeInfo.estPerfCounterFreq;
+
+ } else {
+
+ /*
+ * Compute a counter frequency that will cause virtual time to line
+ * up with system time one second from now, assuming that the
+ * performance counter continues to tick at timeInfo.estPerfCounterFreq.
+ */
+
+ timeInfo.curCounterFreq.QuadPart
+ = 10000000 * timeInfo.estPerfCounterFreq / ( delta + 10000000 );
+
+ /*
+ * Limit frequency excursions to 1000 ppm from estimate
+ */
+
+ if ( timeInfo.curCounterFreq.QuadPart < lowBound ) {
+ timeInfo.curCounterFreq.QuadPart = lowBound;
+ } else if ( timeInfo.curCounterFreq.QuadPart > hiBound ) {
+ timeInfo.curCounterFreq.QuadPart = hiBound;
+ }
+ }
+
+ LeaveCriticalSection( &timeInfo.cs );
+
}
*** ../tcl8.3.2base/src/tcl8.3.2/tests/winTime.test Mon Apr 10 13:19:08 2000
--- ./tcl8.3.2/src/tcl8.3.2/tests/winTime.test Wed Sep 6 14:55:30 2000
***************
*** 33,38 ****
--- 33,64 ----
set result
} {1969}
+ # Next test tries to make sure that the Tcl clock stays in step
+ # with the Windows clock. 3000 iterations really isn't enough,
+ # but how many does a tester have patience for?
+
+ test winTime-2.1 {Synchronization of Tcl and Windows clocks} {pcOnly} {
+ set failed 0
+ foreach { sys_sec sys_usec tcl_sec tcl_usec } [testwinclock] {}
+ set olddiff [expr { abs ( $tcl_sec - $sys_sec
+ + 1.0e-6 * ( $tcl_usec - $sys_usec ) ) }]
+ set ok 1
+ for { set i 0 } { $i < 3000 } { incr i } {
+ foreach { sys_sec sys_usec tcl_sec tcl_usec } [testwinclock] {}
+ set diff [expr { abs ( $tcl_sec - $sys_sec
+ + 1.0e-6 * ( $tcl_usec - $sys_usec ) ) }]
+ if { ( $diff > $olddiff + 1000 )
+ || ( $diff > 11000 ) } {
+ set failed 1
+ break
+ } else {
+ set olddiff $diff
+ after 1
+ }
+ }
+ set failed
+ } {0}
+
# cleanup
::tcltest::cleanupTests
return