diff options
Diffstat (limited to 'win/tclWinTime.c')
| -rw-r--r-- | win/tclWinTime.c | 922 |
1 files changed, 320 insertions, 602 deletions
diff --git a/win/tclWinTime.c b/win/tclWinTime.c index 438a8ec..0163723 100644 --- a/win/tclWinTime.c +++ b/win/tclWinTime.c @@ -4,7 +4,7 @@ * Contains Windows specific versions of Tcl functions that obtain time * values from the operating system. * - * Copyright © 1995-1998 Sun Microsystems, Inc. + * 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. @@ -27,7 +27,6 @@ * month, where index 1 is January. */ -#ifndef TCL_NO_DEPRECATED static const int normalDays[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }; @@ -36,25 +35,22 @@ static const int leapDays[] = { -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; -typedef struct { +typedef struct ThreadSpecificData { char tzName[64]; /* Time zone name */ struct tm tm; /* time information */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; -#endif /* TCL_NO_DEPRECATED */ /* * Data for managing high-resolution timers. */ -typedef struct { +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. */ - 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 @@ -65,6 +61,7 @@ typedef struct { 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: @@ -77,40 +74,35 @@ typedef struct { 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 */ - unsigned long long fileTimeSample[SAMPLES]; + Tcl_WideUInt fileTimeSample[SAMPLES]; /* Last 64 samples of system time. */ - long long perfCounterSample[SAMPLES]; + Tcl_WideInt perfCounterSample[SAMPLES]; /* Last 64 samples of performance counter. */ int sampleNo; /* Current sample number. */ } TimeInfo; static TimeInfo timeInfo = { - { NULL, 0, 0, NULL, NULL, 0 }, + { NULL }, 0, 0, - 1, (HANDLE) NULL, (HANDLE) NULL, (HANDLE) NULL, -#if defined(HAVE_CAST_TO_UNION) && !defined(__cplusplus) - (LARGE_INTEGER) (long long) 0, +#ifdef HAVE_CAST_TO_UNION + (LARGE_INTEGER) (Tcl_WideInt) 0, (ULARGE_INTEGER) (DWORDLONG) 0, - (LARGE_INTEGER) (long long) 0, - (LARGE_INTEGER) (long long) 0, - (LARGE_INTEGER) (long long) 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 }, @@ -118,36 +110,21 @@ static TimeInfo timeInfo = { }; /* - * 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. */ -#ifndef TCL_NO_DEPRECATED static struct tm * ComputeGMT(const time_t *tp); -#endif /* TCL_NO_DEPRECATED */ -static void StopCalibration(void *clientData); +static void StopCalibration(ClientData clientData); static DWORD WINAPI CalibrationThread(LPVOID arg); static void UpdateTimeEachSecond(void); -static void ResetCounterSamples(unsigned long long fileTime, - long long perfCounter, long long perfFreq); -static long long AccumulateSample(long long perfCounter, - unsigned long long fileTime); +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, - void *clientData); -static long long NativeGetMicroseconds(void); + ClientData clientData); static void NativeGetTime(Tcl_Time* timebuf, - void *clientData); + ClientData clientData); /* * TIP #233 (Virtualized Time): Data for the time hooks, if any. @@ -155,24 +132,7 @@ static void NativeGetTime(Tcl_Time* timebuf, Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime; Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime; -void *tclTimeClientData = NULL; - -/* - * Inlined version of Tcl_GetTime. - */ - -static inline void -GetTime( - Tcl_Time *timePtr) -{ - tclGetTimeProcPtr(timePtr, tclTimeClientData); -} - -static inline int -IsTimeNative(void) -{ - return tclGetTimeProcPtr == NativeGetTime; -} +ClientData tclTimeClientData = NULL; /* *---------------------------------------------------------------------- @@ -194,20 +154,10 @@ IsTimeNative(void) unsigned long TclpGetSeconds(void) { - long long usecSincePosixEpoch; + Tcl_Time t; - /* - * Try to use high resolution timer - */ - - if (IsTimeNative() && (usecSincePosixEpoch = NativeGetMicroseconds())) { - return usecSincePosixEpoch / 1000000; - } else { - Tcl_Time t; - - GetTime(&t); - return t.sec; - } + (*tclGetTimeProcPtr) (&t, tclTimeClientData); /* Tcl_GetTime inlined. */ + return t.sec; } /* @@ -218,7 +168,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 dependent. + * start time is also system dependant. * * Results: * Number of clicks from some start time. @@ -232,121 +182,31 @@ TclpGetSeconds(void) unsigned long TclpGetClicks(void) { - long long usecSincePosixEpoch; - /* - * Try to use high resolution timer. + * Use the Tcl_GetTime abstraction to get the time in microseconds, as + * nearly as we can, and return it. */ - if (IsTimeNative() && (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. - */ + Tcl_Time now; /* Current Tcl time */ + unsigned long retval; /* Value to return */ - Tcl_Time now; /* Current Tcl time */ + (*tclGetTimeProcPtr) (&now, tclTimeClientData); /* Tcl_GetTime inlined */ - GetTime(&now); - return ((unsigned long)(now.sec)*1000000UL) + (unsigned long)(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). - * - *---------------------------------------------------------------------- - */ - -long long -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 (long long)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. - * - *---------------------------------------------------------------------- - */ + retval = (now.sec * 1000000) + now.usec; + return retval; -double -TclpWideClickInMicrosec(void) -{ - if (!wideClick.initialized) { - (void) TclpGetWideClicks(); /* initialize */ - } - return wideClick.microsecsScale; } /* *---------------------------------------------------------------------- * - * TclpGetMicroseconds -- + * TclpGetTimeZone -- * - * This procedure returns a WideInt value that represents the highest - * resolution clock in microseconds available on the system. + * Determines the current timezone. The method varies wildly between + * different Platform implementations, so its hidden in this function. * * Results: - * Number of microseconds (from the epoch). + * Minutes west of GMT. * * Side effects: * None. @@ -354,28 +214,16 @@ TclpWideClickInMicrosec(void) *---------------------------------------------------------------------- */ -long long -TclpGetMicroseconds(void) +int +TclpGetTimeZone( + unsigned long currentTime) { - long long usecSincePosixEpoch; + int timeZone; - /* - * Try to use high resolution timer. - */ + tzset(); + timeZone = timezone / 60; - if (IsTimeNative() && (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; - - GetTime(&now); - return (((long long) now.sec) * 1000000) + now.usec; - } + return timeZone; } /* @@ -404,18 +252,7 @@ void Tcl_GetTime( Tcl_Time *timePtr) /* Location to store time information. */ { - long long usecSincePosixEpoch; - - /* - * Try to use high resolution timer. - */ - - if (IsTimeNative() && (usecSincePosixEpoch = NativeGetMicroseconds())) { - timePtr->sec = (long) (usecSincePosixEpoch / 1000000); - timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000); - } else { - GetTime(timePtr); - } + (*tclGetTimeProcPtr) (timePtr, tclTimeClientData); } /* @@ -437,8 +274,8 @@ Tcl_GetTime( static void NativeScaleTime( - TCL_UNUSED(Tcl_Time *), - TCL_UNUSED(void *)) + Tcl_Time *timePtr, + ClientData clientData) { /* * Native scale is 1:1. Nothing is done. @@ -448,104 +285,13 @@ NativeScaleTime( /* *---------------------------------------------------------------------- * - * IsPerfCounterAvailable -- - * - * Tests whether the performance counter is available, which is a gnarly - * problem on 32-bit systems. Also retrieves the nominal frequency of the - * performance counter. - * - * Results: - * 1 if the counter is available, 0 if not. - * - * Side effects: - * Updates fields of the timeInfo global. Make sure you hold the lock - * before calling this. - * - *---------------------------------------------------------------------- - */ - -static inline int -IsPerfCounterAvailable(void) -{ - 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: - * - 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. - * - * 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 !defined(_WIN64) - if (timeInfo.perfCounterAvailable && - /* - * The following lines would do an exact match on crystal - * frequency: - * - * timeInfo.nominalFreq.QuadPart != (long long) 1193182 && - * timeInfo.nominalFreq.QuadPart != (long long) 3579545 && - */ - timeInfo.nominalFreq.QuadPart > (long long) 15000000) { - /* - * As an exception, if every logical processor on the system is on the - * same chip, we use the performance counter anyway, presuming that - * everyone's TSC is locked to the same oscillator. - */ - - SYSTEM_INFO systemInfo; - 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 */ - == (int)systemInfo.dwNumberOfProcessors)) { - timeInfo.perfCounterAvailable = TRUE; - } else { - timeInfo.perfCounterAvailable = FALSE; - } - } -#endif /* above code is Win32 only */ - - return timeInfo.perfCounterAvailable; -} - -/* - *---------------------------------------------------------------------- - * - * NativeGetMicroseconds -- + * NativeGetTime -- * - * Gets the current system time in microseconds since the beginning - * of the epoch: 00:00 UCT, January 1, 1970. + * 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 wide integer with number of microseconds from the epoch, or - * 0 if high resolution timer is not available. + * Returns the current time in timePtr. * * Side effects: * On the first call, initializes a set of static variables to keep track @@ -558,20 +304,15 @@ IsPerfCounterAvailable(void) *---------------------------------------------------------------------- */ -static inline long long -NativeCalc100NsTicks( - ULONGLONG fileTimeLastCall, - LONGLONG perfCounterLastCall, - LONGLONG curCounterFreq, - LONGLONG curCounter) +static void +NativeGetTime( + Tcl_Time *timePtr, + ClientData clientData) { - return fileTimeLastCall + - ((curCounter - perfCounterLastCall) * 10000000 / curCounterFreq); -} + struct _timeb t; + int useFtime = 1; /* Flag == TRUE if we need to fall back on + * ftime rather than using the perf counter. */ -static long long -NativeGetMicroseconds(void) -{ /* * Initialize static storage on the first trip through. * @@ -582,20 +323,84 @@ NativeGetMicroseconds(void) if (!timeInfo.initialized) { TclpInitLock(); if (!timeInfo.initialized) { - timeInfo.posixEpoch.LowPart = 0xD53E8000; - timeInfo.posixEpoch.HighPart = 0x019DB1DE; + 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: + * - 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. + * + * 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 !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, + * presuming that everyone's TSC is locked to the same + * oscillator. + */ + + 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)) { + timeInfo.perfCounterAvailable = TRUE; + } else { + timeInfo.perfCounterAvailable = FALSE; + } + } +#endif /* above code is Win32 only */ /* * If the performance counter is available, start a thread to * calibrate it. */ - if (IsPerfCounterAvailable()) { + if (timeInfo.perfCounterAvailable) { DWORD id; InitializeCriticalSection(&timeInfo.cs); - timeInfo.readyEvent = CreateEventW(NULL, FALSE, FALSE, NULL); - timeInfo.exitEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + 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, @@ -609,7 +414,7 @@ NativeGetMicroseconds(void) WaitForSingleObject(timeInfo.readyEvent, INFINITE); CloseHandle(timeInfo.readyEvent); - Tcl_CreateExitHandler(StopCalibration, NULL); + Tcl_CreateExitHandler(StopCalibration, (ClientData) NULL); } timeInfo.initialized = TRUE; } @@ -622,39 +427,22 @@ NativeGetMicroseconds(void) * time. */ - 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. */ - QueryPerformanceCounter(&curCounter); - - /* - * Hold time section locked as short as possible - */ + posixEpoch.LowPart = 0xD53E8000; + posixEpoch.HighPart = 0x019DB1DE; EnterCriticalSection(&timeInfo.cs); - 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) { - /* - * Calibrated file-time is saved from Posix in 100-ns ticks - */ - - return fileTimeLastCall / 10; - } + QueryPerformanceCounter(&curCounter); /* * If it appears to be more than 1.1 seconds since the last trip @@ -666,65 +454,29 @@ NativeGetMicroseconds(void) * loop should recover. */ - if (curCounter.QuadPart - perfCounterLastCall < - 11 * curCounterFreq * timeInfo.calibrationInterv / 10) { - /* - * Calibrated file-time is saved from Posix in 100-ns ticks. - */ - - return NativeCalc100NsTicks(fileTimeLastCall, - perfCounterLastCall, curCounterFreq, - curCounter.QuadPart) / 10; + 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); + useFtime = 0; } - } - /* - * 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. - * - *---------------------------------------------------------------------- - */ - -static void -NativeGetTime( - Tcl_Time *timePtr, - TCL_UNUSED(void *)) -{ - long long usecSincePosixEpoch; - - /* - * Try to use high resolution timer. - */ + LeaveCriticalSection(&timeInfo.cs); + } - usecSincePosixEpoch = NativeGetMicroseconds(); - if (usecSincePosixEpoch) { - timePtr->sec = (long) (usecSincePosixEpoch / 1000000); - timePtr->usec = (unsigned long) (usecSincePosixEpoch % 1000000); - } else { + if (useFtime) { /* * High resolution timer is not available. Just use ftime. */ - struct _timeb t; - _ftime(&t); - timePtr->sec = (long) t.time; + timePtr->sec = (long)t.time; timePtr->usec = t.millitm * 1000; } } @@ -747,11 +499,9 @@ NativeGetTime( *---------------------------------------------------------------------- */ -void TclWinResetTimerResolution(void); - static void StopCalibration( - TCL_UNUSED(void *)) + ClientData unused) /* Client data is unused */ { SetEvent(timeInfo.exitEvent); @@ -768,6 +518,93 @@ StopCalibration( /* *---------------------------------------------------------------------- * + * TclpGetTZName -- + * + * Gets the current timezone string. + * + * Results: + * Returns a pointer to a static string, or NULL on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +TclpGetTZName( + int dst) +{ + int len; + char *zone, *p; + TIME_ZONE_INFORMATION tz; + Tcl_Encoding encoding; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + char *name = tsdPtr->tzName; + + /* + * 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 + * ezone string, even though env(TZ) is GMT and the variable _timezone + * is 0. + */ + + name[0] = '\0'; + + 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. + */ + + len = strlen(zone); + if (len > 3) { + len = 3; + } + if (dst != 0) { + /* + * Skip the offset string and get the DST string. + */ + + p = zone + len; + p += strspn(p, "+-:0123456789"); + if (*p != '\0') { + zone = p; + len = strlen(zone); + if (len > 3) { + len = 3; + } + } + } + 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 + */ + + dst = 0; + } + encoding = Tcl_GetEncoding(NULL, "unicode"); + 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; +} + +/* + *---------------------------------------------------------------------- + * * TclpGetDate -- * * This function converts between seconds and struct tm. If useGMT is @@ -783,26 +620,15 @@ StopCalibration( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED struct tm * TclpGetDate( - const time_t *t, + CONST time_t *t, int useGMT) { struct tm *tmPtr; time_t time; -#if defined(_WIN64) || defined(_USE_64BIT_TIME_T) -# 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) -# undef timezone /* prevent conflict with timezone() function */ - long timezone = 0; -#endif - tzset(); /* @@ -811,15 +637,28 @@ TclpGetDate( * daylight savings time before the epoch. */ - if (t2 >= 0) { - return TclpLocaltime(&t2); - } + /* + * 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 + */ -#if defined(_MSC_VER) - _get_timezone(&timezone); +#ifdef __BORLANDC__ +#define LOCALTIME_VALIDITY_BOUNDARY SECSPERDAY +#else +#define LOCALTIME_VALIDITY_BOUNDARY 0 #endif - time = t2 - timezone; + if (*t >= LOCALTIME_VALIDITY_BOUNDARY) { + return TclpLocaltime(t); + } + + time = *t - timezone; /* * If we aren't near to overflowing the long, just add the bias and @@ -827,10 +666,10 @@ TclpGetDate( * result at the end. */ - if (t2 < (LONG_MAX - 2*SECSPERDAY) && t2 > (LONG_MIN + 2*SECSPERDAY)) { + if (*t < (LONG_MAX - 2*SECSPERDAY) && *t > (LONG_MIN + 2*SECSPERDAY)) { tmPtr = ComputeGMT(&time); } else { - tmPtr = ComputeGMT(&t2); + tmPtr = ComputeGMT(t); tzset(); @@ -846,14 +685,14 @@ TclpGetDate( time -= 60; } - time = tmPtr->tm_min + time / 60; + time = tmPtr->tm_min + time/60; tmPtr->tm_min = (int)(time % 60); if (tmPtr->tm_min < 0) { tmPtr->tm_min += 60; time -= 60; } - time = tmPtr->tm_hour + time / 60; + time = tmPtr->tm_hour + time/60; tmPtr->tm_hour = (int)(time % 24); if (tmPtr->tm_hour < 0) { tmPtr->tm_hour += 24; @@ -861,12 +700,12 @@ TclpGetDate( } time /= 24; - tmPtr->tm_mday += (int) time; - tmPtr->tm_yday += (int) time; - tmPtr->tm_wday = (tmPtr->tm_wday + (int) time) % 7; + tmPtr->tm_mday += (int)time; + tmPtr->tm_yday += (int)time; + tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7; } } else { - tmPtr = ComputeGMT(&t2); + tmPtr = ComputeGMT(t); } return tmPtr; } @@ -904,8 +743,8 @@ ComputeGMT( * Compute the 4 year span containing the specified time. */ - tmp = (long) (*tp / SECSPER4YEAR); - rem = (long) (*tp % SECSPER4YEAR); + tmp = (long)(*tp / SECSPER4YEAR); + rem = (long)(*tp % SECSPER4YEAR); /* * Correct for weird mod semantics so the remainder is always positive. @@ -972,7 +811,7 @@ ComputeGMT( * Compute day of week. Epoch started on a Thursday. */ - tmPtr->tm_wday = (long) (*tp / SECSPERDAY) + 4; + tmPtr->tm_wday = (long)(*tp / SECSPERDAY) + 4; if ((*tp % SECSPERDAY) < 0) { tmPtr->tm_wday--; } @@ -983,7 +822,6 @@ ComputeGMT( return tmPtr; } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -1013,7 +851,7 @@ ComputeGMT( static DWORD WINAPI CalibrationThread( - TCL_UNUSED(LPVOID)) + LPVOID arg) { FILETIME curFileTime; DWORD waitResult; @@ -1028,12 +866,6 @@ CalibrationThread( 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, timeInfo.curCounterFreq.QuadPart); @@ -1061,6 +893,7 @@ CalibrationThread( UpdateTimeEachSecond(); } + /* lint */ return (DWORD) 0; } @@ -1091,45 +924,29 @@ 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. */ - long long estFreq; /* Estimated perf counter frequency. */ - long long vt0; /* Tcl time right now. */ - long long vt1; /* Tcl time one second from now. */ - long long tdiff; /* Difference between system clock and Tcl + 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. */ - long long driftFreq; /* Frequency needed to drift virtual time into + Tcl_WideInt driftFreq; /* Frequency needed to drift virtual time into * step over 1 second. */ /* - * Sample performance counter and system time (from Posix epoch). + * Sample performance counter and system time. */ + 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)) { - /* - * Look again in next one second. - */ - - return; - } - QueryPerformanceCounter(&curPerfCounter); - lastFileTime.QuadPart = curFileTime.QuadPart; + EnterCriticalSection(&timeInfo.cs); /* - * We divide by timeInfo.curCounterFreq.QuadPart in several places. That + * 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 @@ -1138,6 +955,7 @@ UpdateTimeEachSecond(void) */ if (timeInfo.curCounterFreq.QuadPart == 0){ + LeaveCriticalSection(&timeInfo.cs); timeInfo.perfCounterAvailable = 0; return; } @@ -1157,7 +975,7 @@ UpdateTimeEachSecond(void) */ estFreq = AccumulateSample(curPerfCounter.QuadPart, - (unsigned long long) curFileTime.QuadPart); + (Tcl_WideUInt) curFileTime.QuadPart); /* * We want to adjust things so that time appears to be continuous. @@ -1176,9 +994,11 @@ UpdateTimeEachSecond(void) * is estFreq * 20000000 / (vt1 - vt0) */ - vt0 = NativeCalc100NsTicks(timeInfo.fileTimeLastCall.QuadPart, - timeInfo.perfCounterLastCall.QuadPart, - timeInfo.curCounterFreq.QuadPart, curPerfCounter.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 @@ -1188,111 +1008,21 @@ UpdateTimeEachSecond(void) tdiff = vt0 - curFileTime.QuadPart; if (tdiff > 10000000 || tdiff < -10000000) { - /* - * Jump to current system time, use curent estimated frequency. - */ - - vt0 = curFileTime.QuadPart; + timeInfo.fileTimeLastCall.QuadPart = curFileTime.QuadPart; + timeInfo.curCounterFreq.QuadPart = estFreq; } else { - /* - * 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; + driftFreq = estFreq * 20000000 / (vt1 - vt0); - /* - * 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; - long long 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; - } + if (driftFreq > 1003*estFreq/1000) { + driftFreq = 1003*estFreq/1000; + } else if (driftFreq < 997*estFreq/1000) { + driftFreq = 997*estFreq/1000; } - } - - /* - * 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 = driftFreq; } - timeInfo.fileTimeLastCall.QuadPart = vt0; - timeInfo.curCounterFreq.QuadPart = estFreq; timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart; LeaveCriticalSection(&timeInfo.cs); @@ -1319,13 +1049,12 @@ UpdateTimeEachSecond(void) static void ResetCounterSamples( - unsigned long long fileTime,/* Current file time */ - long long perfCounter, /* Current performance counter */ - long long perfFreq) /* Target performance frequency */ + 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; @@ -1360,22 +1089,20 @@ ResetCounterSamples( * case). */ -static long long +static Tcl_WideInt AccumulateSample( - long long perfCounter, - unsigned long long fileTime) + Tcl_WideInt perfCounter, + Tcl_WideUInt fileTime) { - unsigned long long workFTSample; - /* File time sample being removed from or + Tcl_WideUInt workFTSample; /* File time sample being removed from or * added to the circular buffer. */ - long long workPCSample; /* Performance counter sample being removed + Tcl_WideInt workPCSample; /* Performance counter sample being removed * from or added to the circular buffer. */ - unsigned long long lastFTSample; - /* Last file time sample recorded */ - long long lastPCSample; /* Last performance counter sample recorded */ - long long FTdiff; /* Difference between last FT and current */ - long long PCdiff; /* Difference between last PC and current */ - long long estFreq; /* Estimated performance counter frequency */ + 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. @@ -1409,7 +1136,7 @@ AccumulateSample( estFreq = 10000000 * (perfCounter - workPCSample) / (fileTime - workFTSample); timeInfo.perfCounterSample[timeInfo.sampleNo] = perfCounter; - timeInfo.fileTimeSample[timeInfo.sampleNo] = (long long) fileTime; + timeInfo.fileTimeSample[timeInfo.sampleNo] = (Tcl_WideInt) fileTime; /* * Advance the sample number. @@ -1439,10 +1166,9 @@ AccumulateSample( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED struct tm * TclpGmtime( - const time_t *timePtr) /* Pointer to the number of seconds since the + CONST time_t *timePtr) /* Pointer to the number of seconds since the * local system's epoch */ { /* @@ -1451,11 +1177,7 @@ TclpGmtime( * Posix gmtime_r function. */ -#if defined(_WIN64) || defined(_USE_64BIT_TIME_T) return gmtime(timePtr); -#else - return _gmtime32((const __time32_t *) timePtr); -#endif /* _WIN64 || _USE_64BIT_TIME_T */ } /* @@ -1477,8 +1199,9 @@ TclpGmtime( struct tm * TclpLocaltime( - const time_t *timePtr) /* Pointer to the number of seconds since the + 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 @@ -1486,13 +1209,8 @@ TclpLocaltime( * provide a Posix localtime_r function. */ -#if defined(_WIN64) || defined(_USE_64BIT_TIME_T) return localtime(timePtr); -#else - return _localtime32((const __time32_t *) timePtr); -#endif /* _WIN64 || _USE_64BIT_TIME_T */ } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -1515,7 +1233,7 @@ void Tcl_SetTimeProc( Tcl_GetTimeProc *getProc, Tcl_ScaleTimeProc *scaleProc, - void *clientData) + ClientData clientData) { tclGetTimeProcPtr = getProc; tclScaleTimeProcPtr = scaleProc; @@ -1542,7 +1260,7 @@ void Tcl_QueryTimeProc( Tcl_GetTimeProc **getProc, Tcl_ScaleTimeProc **scaleProc, - void **clientData) + ClientData *clientData) { if (getProc) { *getProc = tclGetTimeProcPtr; |
