diff options
Diffstat (limited to 'win/tclWinTime.c')
-rw-r--r-- | win/tclWinTime.c | 1003 |
1 files changed, 570 insertions, 433 deletions
diff --git a/win/tclWinTime.c b/win/tclWinTime.c index dd5699e..3ae108b 100644 --- a/win/tclWinTime.c +++ b/win/tclWinTime.c @@ -1,29 +1,30 @@ -/* +/* * tclWinTime.c -- * - * Contains Windows specific versions of Tcl functions that - * obtain time values from the operating system. + * Contains Windows specific versions of Tcl functions that obtain time + * values from the operating system. * * Copyright 1995-1998 by Sun Microsystems, Inc. * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ -#include "tclWinInt.h" +#include "tclInt.h" -#define SECSPERDAY (60L * 60L * 24L) -#define SECSPERYEAR (SECSPERDAY * 365L) -#define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY) +#define SECSPERDAY (60L * 60L * 24L) +#define SECSPERYEAR (SECSPERDAY * 365L) +#define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY) /* - * Number of samples over which to estimate the performance counter + * Number of samples over which to estimate the performance counter. */ -#define SAMPLES 64 + +#define SAMPLES 64 /* - * The following arrays contain the day of year for the last day of - * each month, where index 1 is January. + * The following arrays contain the day of year for the last day of each + * month, where index 1 is January. */ static int normalDays[] = { @@ -45,38 +46,29 @@ static Tcl_ThreadDataKey dataKey; */ typedef struct TimeInfo { - - CRITICAL_SECTION cs; /* Mutex guarding this structure */ - + 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 */ - - HANDLE exitEvent; /* Event to signal out of an exit handler - * to tell the calibration loop to - * terminate */ - - LARGE_INTEGER nominalFreq; /* Nominal frequency of the system - * performance counter, that is, the value - * returned from QueryPerformanceFrequency. */ + 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. */ + HANDLE exitEvent; /* Event to signal out of an exit handler to + * tell the calibration loop to terminate. */ + 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: - * lastFileTime + (current perf counter - lastCounter) + * 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. + * and lastFileTime and lastCounter are updated any time that virtual time + * is returned to a caller. */ ULARGE_INTEGER fileTimeLastCall; @@ -84,16 +76,14 @@ typedef struct TimeInfo { LARGE_INTEGER curCounterFreq; /* - * Data used in developing the estimate of performance counter - * frequency + * Data used in developing the estimate of performance counter frequency */ + Tcl_WideUInt fileTimeSample[SAMPLES]; - /* Last 64 samples of system time */ + /* Last 64 samples of system time. */ Tcl_WideInt perfCounterSample[SAMPLES]; - /* Last 64 samples of performance counter */ - int sampleNo; /* Current sample number */ - - + /* Last 64 samples of performance counter. */ + int sampleNo; /* Current sample number. */ } TimeInfo; static TimeInfo timeInfo = { @@ -119,33 +109,38 @@ static TimeInfo timeInfo = { 0 }; -static CONST FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE }; - /* * Declarations for functions defined later in this file. */ -static struct tm * ComputeGMT _ANSI_ARGS_((const time_t *tp)); -static void StopCalibration _ANSI_ARGS_(( ClientData )); -static DWORD WINAPI CalibrationThread _ANSI_ARGS_(( LPVOID arg )); -static void UpdateTimeEachSecond _ANSI_ARGS_(( void )); -static void ResetCounterSamples _ANSI_ARGS_(( - Tcl_WideUInt fileTime, - Tcl_WideInt perfCounter, - Tcl_WideInt perfFreq - )); -static Tcl_WideInt AccumulateSample _ANSI_ARGS_(( - Tcl_WideInt perfCounter, - Tcl_WideUInt fileTime - )); +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 void NativeGetTime(Tcl_Time* timebuf, + ClientData clientData); + +/* + * TIP #233 (Virtualized Time): Data for the time hooks, if any. + */ + +Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime; +Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime; +ClientData tclTimeClientData = NULL; /* *---------------------------------------------------------------------- * * TclpGetSeconds -- * - * This procedure returns the number of seconds from the epoch. - * On most Unix systems the epoch is Midnight Jan 1, 1970 GMT. + * This procedure returns the number of seconds from the epoch. On most + * Unix systems the epoch is Midnight Jan 1, 1970 GMT. * * Results: * Number of seconds from the epoch. @@ -157,10 +152,11 @@ static Tcl_WideInt AccumulateSample _ANSI_ARGS_(( */ unsigned long -TclpGetSeconds() +TclpGetSeconds(void) { Tcl_Time t; - Tcl_GetTime( &t ); + + (*tclGetTimeProcPtr) (&t, tclTimeClientData); /* Tcl_GetTime inlined. */ return t.sec; } @@ -169,11 +165,10 @@ TclpGetSeconds() * * TclpGetClicks -- * - * 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. + * 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. * * Results: * Number of clicks from some start time. @@ -185,18 +180,19 @@ TclpGetSeconds() */ unsigned long -TclpGetClicks() +TclpGetClicks(void) { /* - * Use the Tcl_GetTime abstraction to get the time in microseconds, - * as nearly as we can, and return it. + * Use the Tcl_GetTime 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 */ - Tcl_GetTime( &now ); - retval = ( now.sec * 1000000 ) + now.usec; + (*tclGetTimeProcPtr) (&now, tclTimeClientData); /* Tcl_GetTime inlined */ + + retval = (now.sec * 1000000) + now.usec; return retval; } @@ -206,9 +202,8 @@ TclpGetClicks() * * TclpGetTimeZone -- * - * Determines the current timezone. The method varies wildly - * between different Platform implementations, so its hidden in - * this function. + * Determines the current timezone. The method varies wildly between + * different Platform implementations, so its hidden in this function. * * Results: * Minutes west of GMT. @@ -220,13 +215,13 @@ TclpGetClicks() */ int -TclpGetTimeZone (currentTime) - Tcl_WideInt currentTime; +TclpGetTimeZone( + unsigned long currentTime) { int timeZone; tzset(); - timeZone = _timezone / 60; + timeZone = timezone / 60; return timeZone; } @@ -236,79 +231,137 @@ TclpGetTimeZone (currentTime) * * Tcl_GetTime -- * - * 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 seconds and microseconds since the + * beginning of the epoch: 00:00 UCT, January 1, 1970. * * Results: * 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. + * 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. * *---------------------------------------------------------------------- */ void -Tcl_GetTime(timePtr) - Tcl_Time *timePtr; /* Location to store time information. */ +Tcl_GetTime( + Tcl_Time *timePtr) /* Location to store time information. */ { - struct timeb t; + (*tclGetTimeProcPtr) (timePtr, tclTimeClientData); +} + +/* + *---------------------------------------------------------------------- + * + * NativeScaleTime -- + * + * TIP #233: Scale from virtual time to the real-time. For native scaling + * the relationship is 1:1 and nothing has to be done. + * + * Results: + * Scales the time in timePtr. + * + * Side effects: + * See above. + * + *---------------------------------------------------------------------- + */ - int useFtime = 1; /* Flag == TRUE if we need to fall back - * on ftime rather than using the perf - * counter */ +static void +NativeScaleTime( + Tcl_Time *timePtr, + ClientData clientData) +{ + /* + * Native scale is 1:1. Nothing is done. + */ +} + +/* + *---------------------------------------------------------------------- + * + * 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: + * 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. + * + *---------------------------------------------------------------------- + */ - /* Initialize static storage on the first trip through. */ +static void +NativeGetTime( + Tcl_Time *timePtr, + ClientData clientData) +{ + struct timeb t; + int useFtime = 1; /* Flag == TRUE if we need to fall back on + * ftime rather than using the perf counter. */ /* - * Note: Outer check for 'initialized' is a performance win - * since it avoids an extra mutex lock in the common case. + * 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 ) { + if (!timeInfo.initialized) { TclpInitLock(); - if ( !timeInfo.initialized ) { - timeInfo.perfCounterAvailable - = QueryPerformanceFrequency( &timeInfo.nominalFreq ); + if (!timeInfo.initialized) { + timeInfo.perfCounterAvailable = + QueryPerformanceFrequency(&timeInfo.nominalFreq); /* - * Some hardware abstraction layers use the CPU clock - * in place of the real-time clock as a performance counter - * reference. This results in: + * 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. + * - 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. + * 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. + * + * A sloppier but serviceable heuristic is that the RTC crystal is + * normally less than 15 MHz while the TSC crystal is virtually + * assured to be greater than 100 MHz. Since Win98SE appears to + * fiddle with the definition of the perf counter frequency + * (perhaps in an attempt to calibrate the clock?), we use the + * latter rule rather than an exact match. * - * A sloppier but serviceable heuristic is that the RTC crystal - * is normally less than 15 MHz while the TSC crystal is - * virtually assured to be greater than 100 MHz. Since Win98SE - * appears to fiddle with the definition of the perf counter - * frequency (perhaps in an attempt to calibrate the clock?) - * we use the latter rule rather than an exact match. + * We also assume (perhaps questionably) that the vendors have + * gotten their act together on Win64, so bypass all this rubbish + * on that platform. */ - if ( timeInfo.perfCounterAvailable - /* The following lines would do an exact match on - * crystal frequency: - * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt) 1193182 - * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt) 3579545 - */ - && timeInfo.nominalFreq.QuadPart > (Tcl_WideInt) 15000000 ) { - +#if !defined(_WIN64) + if (timeInfo.perfCounterAvailable + /* + * The following lines would do an exact match on crystal + * frequency: + * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt)1193182 + * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt)3579545 + */ + && timeInfo.nominalFreq.QuadPart > (Tcl_WideInt) 15000000){ /* * As an exception, if every logical processor on the system * is on the same chip, we use the performance counter anyway, @@ -318,118 +371,109 @@ Tcl_GetTime(timePtr) SYSTEM_INFO systemInfo; unsigned int regs[4]; - GetSystemInfo( &systemInfo ); - if ( TclWinCPUID( 0, regs ) == TCL_OK - - && regs[1] == 0x756e6547 /* "Genu" */ - && regs[3] == 0x49656e69 /* "ineI" */ - && regs[2] == 0x6c65746e /* "ntel" */ - - && TclWinCPUID( 1, regs ) == TCL_OK - - && ( (regs[0] & 0x00000F00) == 0x00000F00 /* Pentium 4 */ - || ( (regs[0] & 0x00F00000) /* Extended family */ - && (regs[3] & 0x10000000) ) ) /* Hyperthread */ - && ( ( ( regs[1] & 0x00FF0000 ) >> 16 ) /* CPU count */ - == systemInfo.dwNumberOfProcessors ) - ) { + GetSystemInfo(&systemInfo); + if (TclWinCPUID(0, regs) == TCL_OK + && regs[1] == 0x756e6547 /* "Genu" */ + && regs[3] == 0x49656e69 /* "ineI" */ + && regs[2] == 0x6c65746e /* "ntel" */ + && TclWinCPUID(1, regs) == TCL_OK + && ((regs[0]&0x00000F00) == 0x00000F00 /* Pentium 4 */ + || ((regs[0] & 0x00F00000) /* Extended family */ + && (regs[3] & 0x10000000))) /* Hyperthread */ + && (((regs[1]&0x00FF0000) >> 16)/* CPU count */ + == systemInfo.dwNumberOfProcessors)) { timeInfo.perfCounterAvailable = TRUE; } else { - timeInfo.perfCounterAvailable = FALSE; - } - + timeInfo.perfCounterAvailable = FALSE; + } } +#endif /* above code is Win32 only */ /* * If the performance counter is available, start a thread to * calibrate it. */ - if ( timeInfo.perfCounterAvailable ) { + if (timeInfo.perfCounterAvailable) { DWORD id; - InitializeCriticalSection( &timeInfo.cs ); - timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - timeInfo.calibrationThread = CreateThread( NULL, - 256, - CalibrationThread, - (LPVOID) NULL, - 0, - &id ); - SetThreadPriority( timeInfo.calibrationThread, - THREAD_PRIORITY_HIGHEST ); + + InitializeCriticalSection(&timeInfo.cs); + timeInfo.readyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + timeInfo.exitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + timeInfo.calibrationThread = CreateThread(NULL, 256, + CalibrationThread, (LPVOID) NULL, 0, &id); + SetThreadPriority(timeInfo.calibrationThread, + THREAD_PRIORITY_HIGHEST); /* - * Wait for the thread just launched to start running, - * and create an exit handler that kills it so that it - * doesn't outlive unloading tclXX.dll + * Wait for the thread just launched to start running, and + * create an exit handler that kills it so that it doesn't + * outlive unloading tclXX.dll */ - WaitForSingleObject( timeInfo.readyEvent, INFINITE ); - CloseHandle( timeInfo.readyEvent ); - Tcl_CreateExitHandler( StopCalibration, (ClientData) NULL ); + WaitForSingleObject(timeInfo.readyEvent, INFINITE); + CloseHandle(timeInfo.readyEvent); + Tcl_CreateExitHandler(StopCalibration, (ClientData) NULL); } timeInfo.initialized = TRUE; } TclpInitUnlock(); } - if ( timeInfo.perfCounterAvailable ) { + if (timeInfo.perfCounterAvailable && timeInfo.curCounterFreq.QuadPart!=0) { /* - * Query the performance counter and use it to calculate the - * current time. + * Query the performance counter and use it to calculate the current + * time. */ LARGE_INTEGER curCounter; - /* Current performance counter */ - - Tcl_WideInt curFileTime; - /* Current estimated time, expressed - * as 100-ns ticks since the Windows epoch */ - + /* 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 */ - + /* Posix epoch expressed as 100-ns ticks since + * the windows epoch. */ Tcl_WideInt usecSincePosixEpoch; - /* Current microseconds since Posix epoch */ + /* Current microseconds since Posix epoch. */ posixEpoch.LowPart = 0xD53E8000; posixEpoch.HighPart = 0x019DB1DE; - EnterCriticalSection( &timeInfo.cs ); + EnterCriticalSection(&timeInfo.cs); - QueryPerformanceCounter( &curCounter ); + QueryPerformanceCounter(&curCounter); - /* + /* * If it appears to be more than 1.1 seconds since the last trip - * through the calibration loop, the performance counter may - * have jumped forward. (See MSDN Knowledge Base article - * Q274323 for a description of the hardware problem that makes - * this test necessary.) If the counter jumps, we don't want - * to use it directly. Instead, we must return system time. - * Eventually, the calibration loop should recover. + * through the calibration loop, the performance counter may have + * jumped forward. (See MSDN Knowledge Base article Q274323 for a + * description of the hardware problem that makes this test + * necessary.) If the counter jumps, we don't want to use it directly. + * Instead, we must return system time. Eventually, the calibration + * loop should recover. */ - if ( curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart - < 11 * timeInfo.curCounterFreq.QuadPart / 10 ) { - curFileTime = timeInfo.fileTimeLastCall.QuadPart - + ( ( curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart ) - * 10000000 / timeInfo.curCounterFreq.QuadPart ); + if (curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart < + 11 * timeInfo.curCounterFreq.QuadPart / 10) { + curFileTime = timeInfo.fileTimeLastCall.QuadPart + + ((curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart) + * 10000000 / timeInfo.curCounterFreq.QuadPart); timeInfo.fileTimeLastCall.QuadPart = curFileTime; timeInfo.perfCounterLastCall.QuadPart = curCounter.QuadPart; - usecSincePosixEpoch = ( curFileTime - posixEpoch.QuadPart ) / 10; - timePtr->sec = (long) ( usecSincePosixEpoch / 1000000 ); - timePtr->usec = (unsigned long ) ( usecSincePosixEpoch % 1000000 ); + usecSincePosixEpoch = (curFileTime - posixEpoch.QuadPart) / 10; + timePtr->sec = (long) (usecSincePosixEpoch / 1000000); + timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000); useFtime = 0; } - LeaveCriticalSection( &timeInfo.cs ); + LeaveCriticalSection(&timeInfo.cs); } - if ( useFtime ) { - /* High resolution timer is not available. Just use ftime */ + if (useFtime) { + /* + * High resolution timer is not available. Just use ftime. + */ ftime(&t); timePtr->sec = (long)t.time; @@ -449,20 +493,26 @@ Tcl_GetTime(timePtr) * None. * * Side effects: - * Sets the 'exitEvent' event in the 'timeInfo' structure to ask - * the thread in question to exit, and waits for it to do so. + * Sets the 'exitEvent' event in the 'timeInfo' structure to ask the + * thread in question to exit, and waits for it to do so. * *---------------------------------------------------------------------- */ static void -StopCalibration( ClientData unused ) - /* Client data is unused */ +StopCalibration( + ClientData unused) /* Client data is unused */ { - SetEvent( timeInfo.exitEvent ); - WaitForSingleObject( timeInfo.calibrationThread, INFINITE ); - CloseHandle( timeInfo.exitEvent ); - CloseHandle( timeInfo.calibrationThread ); + SetEvent(timeInfo.exitEvent); + + /* + * If Tcl_Finalize was called from DllMain, the calibration thread is in a + * paused state so we need to timeout and continue. + */ + + WaitForSingleObject(timeInfo.calibrationThread, 100); + CloseHandle(timeInfo.exitEvent); + CloseHandle(timeInfo.calibrationThread); } /* @@ -482,9 +532,10 @@ StopCalibration( ClientData unused ) */ char * -TclpGetTZName(int dst) +TclpGetTZName( + int dst) { - size_t len; + int len; char *zone, *p; TIME_ZONE_INFORMATION tz; Tcl_Encoding encoding; @@ -495,9 +546,9 @@ TclpGetTZName(int dst) * tzset() under Borland doesn't seem to set up tzname[] at all. * tzset() under MSVC has the following weird observed behavior: * First time we call "clock format [clock seconds] -format %Z -gmt 1" - * we get "GMT", but on all subsequent calls we get the current time - * zone string, even though env(TZ) is GMT and the variable _timezone - * is 0. + * we get "GMT", but on all subsequent calls we get the current time + * ezone string, even though env(TZ) is GMT and the variable _timezone + * is 0. */ name[0] = '\0'; @@ -505,11 +556,10 @@ TclpGetTZName(int dst) zone = getenv("TZ"); if (zone != NULL) { /* - * TZ is of form "NST-4:30NDT", where "NST" would be the - * name of the standard time zone for this area, "-4:30" is - * the offset from GMT in hours, and "NDT is the name of - * the daylight savings time zone in this area. The offset - * and DST strings are optional. + * TZ is of form "NST-4:30NDT", where "NST" would be the name of the + * standard time zone for this area, "-4:30" is the offset from GMT in + * hours, and "NDT is the name of the daylight savings time zone in + * this area. The offset and DST strings are optional. */ len = strlen(zone); @@ -531,23 +581,24 @@ TclpGetTZName(int dst) } } } - Tcl_ExternalToUtf(NULL, NULL, zone, (int)len, 0, NULL, name, + Tcl_ExternalToUtf(NULL, NULL, zone, len, 0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL); } if (name[0] == '\0') { if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_UNKNOWN) { /* - * MSDN: On NT this is returned if DST is not used in - * the current TZ + * MSDN: On NT this is returned if DST is not used in the current + * TZ */ + dst = 0; } encoding = Tcl_GetEncoding(NULL, "unicode"); - Tcl_ExternalToUtf(NULL, encoding, - (char *) ((dst) ? tz.DaylightName : tz.StandardName), -1, + Tcl_ExternalToUtf(NULL, encoding, + (char *) ((dst) ? tz.DaylightName : tz.StandardName), -1, 0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL); Tcl_FreeEncoding(encoding); - } + } return name; } @@ -556,9 +607,9 @@ TclpGetTZName(int dst) * * TclpGetDate -- * - * This function converts between seconds and struct tm. If - * useGMT is true, then the returned date will be in Greenwich - * Mean Time (GMT). Otherwise, it will be in the local time zone. + * This function converts between seconds and struct tm. If useGMT is + * true, then the returned date will be in Greenwich Mean Time (GMT). + * Otherwise, it will be in the local time zone. * * Results: * Returns a static tm structure. @@ -570,11 +621,10 @@ TclpGetTZName(int dst) */ struct tm * -TclpGetDate(t, useGMT) - TclpTime_t t; - int useGMT; +TclpGetDate( + CONST time_t *t, + int useGMT) { - const time_t *tp = (const time_t *) t; struct tm *tmPtr; time_t time; @@ -582,28 +632,44 @@ TclpGetDate(t, useGMT) tzset(); /* - * If we are in the valid range, let the C run-time library - * handle it. Otherwise we need to fake it. Note that this - * algorithm ignores daylight savings time before the epoch. + * If we are in the valid range, let the C run-time library handle it. + * Otherwise we need to fake it. Note that this algorithm ignores + * daylight savings time before the epoch. */ - if (*tp >= 0) { - return localtime(tp); + /* + * Hm, Borland's localtime manages to return NULL under certain + * circumstances (e.g. wintime.test, test 1.2). Nobody tests for this, + * since 'localtime' isn't supposed to do this, possibly leading to + * crashes. + * + * Patch: We only call this function if we are at least one day into + * the epoch, else we handle it ourselves (like we do for times < 0). + * H. Giese, June 2003 + */ + +#ifdef __BORLANDC__ +#define LOCALTIME_VALIDITY_BOUNDARY SECSPERDAY +#else +#define LOCALTIME_VALIDITY_BOUNDARY 0 +#endif + + if (*t >= LOCALTIME_VALIDITY_BOUNDARY) { + return TclpLocaltime(t); } - time = *tp - _timezone; + time = *t - timezone; /* * If we aren't near to overflowing the long, just add the bias and - * use the normal calculation. Otherwise we will need to adjust - * the result at the end. + * use the normal calculation. Otherwise we will need to adjust the + * result at the end. */ - if (*tp < (LONG_MAX - 2 * SECSPERDAY) - && *tp > (LONG_MIN + 2 * SECSPERDAY)) { + if (*t < (LONG_MAX - 2*SECSPERDAY) && *t > (LONG_MIN + 2*SECSPERDAY)) { tmPtr = ComputeGMT(&time); } else { - tmPtr = ComputeGMT(tp); + tmPtr = ComputeGMT(t); tzset(); @@ -612,7 +678,7 @@ TclpGetDate(t, useGMT) * Propagate seconds overflow into minutes, hours and days. */ - time = tmPtr->tm_sec - _timezone; + time = tmPtr->tm_sec - timezone; tmPtr->tm_sec = (int)(time % 60); if (tmPtr->tm_sec < 0) { tmPtr->tm_sec += 60; @@ -639,7 +705,7 @@ TclpGetDate(t, useGMT) tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7; } } else { - tmPtr = ComputeGMT(tp); + tmPtr = ComputeGMT(t); } return tmPtr; } @@ -649,8 +715,8 @@ TclpGetDate(t, useGMT) * * ComputeGMT -- * - * This function computes GMT given the number of seconds since - * the epoch (midnight Jan 1 1970). + * This function computes GMT given the number of seconds since the epoch + * (midnight Jan 1 1970). * * Results: * Returns a (per thread) statically allocated struct tm. @@ -662,8 +728,8 @@ TclpGetDate(t, useGMT) */ static struct tm * -ComputeGMT(tp) - const time_t *tp; +ComputeGMT( + const time_t *tp) { struct tm *tmPtr; long tmp, rem; @@ -678,7 +744,7 @@ ComputeGMT(tp) */ tmp = (long)(*tp / SECSPER4YEAR); - rem = (LONG)(*tp % SECSPER4YEAR); + rem = (long)(*tp % SECSPER4YEAR); /* * Correct for weird mod semantics so the remainder is always positive. @@ -690,9 +756,9 @@ ComputeGMT(tp) } /* - * Compute the year after 1900 by taking the 4 year span and adjusting - * for the remainder. This works because 2000 is a leap year, and - * 1900/2100 are out of the range. + * Compute the year after 1900 by taking the 4 year span and adjusting for + * the remainder. This works because 2000 is a leap year, and 1900/2100 + * are out of the range. */ tmp = (tmp * 4) + 70; @@ -714,13 +780,13 @@ ComputeGMT(tp) tmPtr->tm_year = tmp; /* - * Compute the day of year and leave the seconds in the current day in - * the remainder. + * Compute the day of year and leave the seconds in the current day in the + * remainder. */ tmPtr->tm_yday = rem / SECSPERDAY; rem %= SECSPERDAY; - + /* * Compute the time of day. */ @@ -736,6 +802,7 @@ ComputeGMT(tp) days = (isLeap) ? leapDays : normalDays; for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) { + /* empty body */ } tmPtr->tm_mon = --tmp; tmPtr->tm_mday = tmPtr->tm_yday - days[tmp]; @@ -761,60 +828,66 @@ ComputeGMT(tp) * * CalibrationThread -- * - * Thread that manages calibration of the hi-resolution time - * derived from the performance counter, to keep it synchronized - * with the system clock. + * 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. + * arg - Client data from the CreateThread call. This parameter points to + * the static TimeInfo structure. * * Return value: - * None. This thread embeds an infinite loop. + * None. This thread embeds an infinite loop. * * Side effects: - * At an interval of 1 s, this thread performs virtual time discipline. + * At an interval of 1s, 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. + * 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 ) +CalibrationThread( + LPVOID arg) { FILETIME curFileTime; DWORD waitResult; - /* Get initial system time and performance counter */ + /* + * Get initial system time and performance counter. + */ - GetSystemTimeAsFileTime( &curFileTime ); - QueryPerformanceCounter( &timeInfo.perfCounterLastCall ); - QueryPerformanceFrequency( &timeInfo.curCounterFreq ); + GetSystemTimeAsFileTime(&curFileTime); + QueryPerformanceCounter(&timeInfo.perfCounterLastCall); + QueryPerformanceFrequency(&timeInfo.curCounterFreq); timeInfo.fileTimeLastCall.LowPart = curFileTime.dwLowDateTime; timeInfo.fileTimeLastCall.HighPart = curFileTime.dwHighDateTime; - ResetCounterSamples( timeInfo.fileTimeLastCall.QuadPart, - timeInfo.perfCounterLastCall.QuadPart, - timeInfo.curCounterFreq.QuadPart ); + ResetCounterSamples(timeInfo.fileTimeLastCall.QuadPart, + timeInfo.perfCounterLastCall.QuadPart, + timeInfo.curCounterFreq.QuadPart); /* - * Wake up the calling thread. When it wakes up, it will release the + * Wake up the calling thread. When it wakes up, it will release the * initialization lock. */ - SetEvent( timeInfo.readyEvent ); + SetEvent(timeInfo.readyEvent); - /* Run the calibration once a second */ - - for ( ; ; ) { + /* + * Run the calibration once a second. + */ - /* If the exitEvent is set, break out of the loop. */ + while (timeInfo.perfCounterAvailable) { + /* + * If the exitEvent is set, break out of the loop. + */ waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE); - if ( waitResult == WAIT_OBJECT_0 ) { + if (waitResult == WAIT_OBJECT_0) { break; } UpdateTimeEachSecond(); @@ -829,11 +902,11 @@ CalibrationThread( LPVOID arg ) * * UpdateTimeEachSecond -- * - * Callback from the waitable timer in the clock calibration thread - * that updates system time. + * Callback from the waitable timer in the clock calibration thread that + * updates system time. * * Parameters: - * info -- Pointer to the static TimeInfo structure + * info - Pointer to the static TimeInfo structure * * Results: * None. @@ -845,113 +918,116 @@ CalibrationThread( LPVOID arg ) */ static void -UpdateTimeEachSecond() +UpdateTimeEachSecond(void) { - LARGE_INTEGER curPerfCounter; /* Current value returned from - * QueryPerformanceCounter */ - - FILETIME curSysTime; /* Current system time */ - - LARGE_INTEGER curFileTime; /* File time at the time this callback - * was scheduled. */ - - Tcl_WideInt estFreq; /* Estimated perf counter frequency */ - - Tcl_WideInt vt0; /* Tcl time right now */ - Tcl_WideInt vt1; /* Tcl time one second from now */ - - Tcl_WideInt tdiff; /* Difference between system clock and - * Tcl time. */ - - Tcl_WideInt driftFreq; /* Frequency needed to drift virtual time - * into step over 1 second */ + * QueryPerformanceCounter. */ + FILETIME curSysTime; /* Current system time. */ + LARGE_INTEGER curFileTime; /* File time at the time this callback was + * scheduled. */ + Tcl_WideInt estFreq; /* Estimated perf counter frequency. */ + Tcl_WideInt vt0; /* Tcl time right now. */ + Tcl_WideInt vt1; /* Tcl time one second from now. */ + Tcl_WideInt tdiff; /* Difference between system clock and Tcl + * time. */ + Tcl_WideInt driftFreq; /* Frequency needed to drift virtual time into + * step over 1 second. */ /* * Sample performance counter and system time. */ - QueryPerformanceCounter( &curPerfCounter ); - GetSystemTimeAsFileTime( &curSysTime ); + QueryPerformanceCounter(&curPerfCounter); + GetSystemTimeAsFileTime(&curSysTime); curFileTime.LowPart = curSysTime.dwLowDateTime; curFileTime.HighPart = curSysTime.dwHighDateTime; - EnterCriticalSection( &timeInfo.cs ); + EnterCriticalSection(&timeInfo.cs); /* - * 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. + * We devide by timeInfo.curCounterFreq.QuadPart in several places. That + * value should always be positive on a correctly functioning system. But + * it is good to be defensive about such matters. So if something goes + * wrong and the value does goes to zero, we clear the + * timeInfo.perfCounterAvailable in order to cause the calibration thread + * to shut itself down, then return without additional processing. */ + if (timeInfo.curCounterFreq.QuadPart == 0){ + LeaveCriticalSection(&timeInfo.cs); + timeInfo.perfCounterAvailable = 0; + return; + } + /* - * Store the current sample into the circular buffer of samples, - * and estimate the performance counter frequency. + * 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. */ - estFreq = AccumulateSample( curPerfCounter.QuadPart, - (Tcl_WideUInt) curFileTime.QuadPart ); + estFreq = AccumulateSample(curPerfCounter.QuadPart, + (Tcl_WideUInt) curFileTime.QuadPart); /* * We want to adjust things so that time appears to be continuous. - * Virtual file time, right now, is + * Virtual file time, right now, is * - * vt0 = 10000000 * ( curPerfCounter - perfCounterLastCall ) - * / curCounterFreq - * + fileTimeLastCall + * vt0 = 10000000 * (curPerfCounter - perfCounterLastCall) + * / curCounterFreq + * + fileTimeLastCall * - * 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 + * 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 - * - * The frequency that we need to use to drift the counter back into - * place is estFreq * 20000000 / ( vt1 - vt0 ) + * + * The frequency that we need to use to drift the counter back into place + * is estFreq * 20000000 / (vt1 - vt0) */ - - vt0 = 10000000 * ( curPerfCounter.QuadPart - - timeInfo.perfCounterLastCall.QuadPart ) - / timeInfo.curCounterFreq.QuadPart - + timeInfo.fileTimeLastCall.QuadPart; + + vt0 = 10000000 * (curPerfCounter.QuadPart + - timeInfo.perfCounterLastCall.QuadPart) + / timeInfo.curCounterFreq.QuadPart + + timeInfo.fileTimeLastCall.QuadPart; vt1 = 20000000 + curFileTime.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, compute the drift frequency and - * fill in everything. + * 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, + * compute the drift frequency and fill in everything. */ tdiff = vt0 - curFileTime.QuadPart; - if ( tdiff > 10000000 || tdiff < -10000000 ) { + if (tdiff > 10000000 || tdiff < -10000000) { timeInfo.fileTimeLastCall.QuadPart = curFileTime.QuadPart; timeInfo.curCounterFreq.QuadPart = estFreq; } else { - driftFreq = estFreq * 20000000 / ( vt1 - vt0 ); - if ( driftFreq > 1003 * estFreq / 1000 ) { - driftFreq = 1003 * estFreq / 1000; - } - if ( driftFreq < 997 * estFreq / 1000 ) { - driftFreq = 997 * estFreq / 1000; + driftFreq = estFreq * 20000000 / (vt1 - vt0); + + 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; } timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart; - LeaveCriticalSection( &timeInfo.cs ); - + LeaveCriticalSection(&timeInfo.cs); } - + /* *---------------------------------------------------------------------- * @@ -964,23 +1040,21 @@ UpdateTimeEachSecond() * 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. + * 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 */ +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 ) { + for (i=SAMPLES-1 ; i>=0 ; --i) { timeInfo.perfCounterSample[i] = perfCounter; timeInfo.fileTimeSample[i] = fileTime; perfCounter -= perfFreq; @@ -994,87 +1068,84 @@ ResetCounterSamples( Tcl_WideUInt fileTime, * * AccumulateSample -- * - * Updates the circular buffer of performance counter and system - * time samples with a new data point. + * 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). + * 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 ) +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 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. */ + /* + * 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 ]; + 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 ]; + 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 (PCdiff < timeInfo.nominalFreq.QuadPart * 9 / 10 + || PCdiff > timeInfo.nominalFreq.QuadPart * 11 / 10 + || FTdiff < 9000000 || FTdiff > 11000000) { + ResetCounterSamples(fileTime, perfCounter, + timeInfo.nominalFreq.QuadPart); 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 ) { + /* + * 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; } } @@ -1084,8 +1155,7 @@ AccumulateSample( Tcl_WideInt perfCounter, * * TclpGmtime -- * - * Wrapper around the 'gmtime' library function to make it thread - * safe. + * Wrapper around the 'gmtime' library function to make it thread safe. * * Results: * Returns a pointer to a 'struct tm' in thread-specific data. @@ -1097,18 +1167,17 @@ AccumulateSample( Tcl_WideInt perfCounter, */ struct tm * -TclpGmtime( tt ) - TclpTime_t_CONST tt; +TclpGmtime( + CONST time_t *timePtr) /* Pointer to the number of seconds since the + * local system's epoch */ { - CONST time_t *timePtr = (CONST time_t *) tt; - /* Pointer to the number of seconds - * since the local system's epoch */ /* - * The MS implementation of gmtime is thread safe because - * it returns the time in a block of thread-local storage, - * and Windows does not provide a Posix gmtime_r function. + * The MS implementation of gmtime is thread safe because it returns the + * time in a block of thread-local storage, and Windows does not provide a + * Posix gmtime_r function. */ - return gmtime( timePtr ); + + return gmtime(timePtr); } /* @@ -1129,17 +1198,85 @@ TclpGmtime( tt ) */ struct tm * -TclpLocaltime( tt ) - TclpTime_t_CONST tt; -{ - CONST time_t *timePtr = (CONST time_t *) tt; - /* Pointer to the number of seconds - * since the local system's epoch */ +TclpLocaltime( + CONST time_t *timePtr) /* Pointer to the number of seconds since the + * local system's epoch */ +{ /* - * The MS implementation of localtime is thread safe because - * it returns the time in a block of thread-local storage, - * and Windows does not provide a Posix localtime_r function. + * The MS implementation of localtime is thread safe because it returns + * the time in a block of thread-local storage, and Windows does not + * provide a Posix localtime_r function. */ - return localtime( timePtr ); + + return localtime(timePtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_SetTimeProc -- + * + * TIP #233 (Virtualized Time): Registers two handlers for the + * virtualization of Tcl's access to time information. + * + * Results: + * None. + * + * Side effects: + * Remembers the handlers, alters core behaviour. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_SetTimeProc( + Tcl_GetTimeProc *getProc, + Tcl_ScaleTimeProc *scaleProc, + ClientData clientData) +{ + tclGetTimeProcPtr = getProc; + tclScaleTimeProcPtr = scaleProc; + tclTimeClientData = clientData; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_QueryTimeProc -- + * + * TIP #233 (Virtualized Time): Query which time handlers are registered. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_QueryTimeProc( + Tcl_GetTimeProc **getProc, + Tcl_ScaleTimeProc **scaleProc, + ClientData *clientData) +{ + if (getProc) { + *getProc = tclGetTimeProcPtr; + } + if (scaleProc) { + *scaleProc = tclScaleTimeProcPtr; + } + if (clientData) { + *clientData = tclTimeClientData; + } } + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |