summaryrefslogtreecommitdiffstats
path: root/win/tclWinTime.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinTime.c')
-rw-r--r--win/tclWinTime.c1112
1 files changed, 715 insertions, 397 deletions
diff --git a/win/tclWinTime.c b/win/tclWinTime.c
index d12afdf..7045c72 100644
--- a/win/tclWinTime.c
+++ b/win/tclWinTime.c
@@ -1,33 +1,37 @@
-/*
+/*
* 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.
- *
- * RCS: @(#) $Id: tclWinTime.c,v 1.8 2001/09/06 11:48:44 davygrvy Exp $
+ * 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)
+
+/*
+ * Number of samples over which to estimate the performance counter.
+ */
-#define SECSPERDAY (60L * 60L * 24L)
-#define SECSPERYEAR (SECSPERDAY * 365L)
-#define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY)
+#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[] = {
+static const int normalDays[] = {
-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
};
-static int leapDays[] = {
+static const int leapDays[] = {
-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
@@ -38,85 +42,105 @@ typedef struct ThreadSpecificData {
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 */
-
+ 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 */
+ 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 lastFileTime;
- LARGE_INTEGER lastCounter;
+ ULARGE_INTEGER fileTimeLastCall;
+ LARGE_INTEGER perfCounterLastCall;
LARGE_INTEGER curCounterFreq;
- /*
- * The next two values are used only in the calibration thread, to track
- * the frequency of the performance counter.
+ /*
+ * Data used in developing the estimate of performance counter frequency
*/
- 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 */
-
+ Tcl_WideUInt fileTimeSample[SAMPLES];
+ /* Last 64 samples of system time. */
+ Tcl_WideInt perfCounterSample[SAMPLES];
+ /* Last 64 samples of performance counter. */
+ int sampleNo; /* Current sample number. */
} TimeInfo;
static TimeInfo timeInfo = {
- NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0
+ { NULL, 0, 0, NULL, NULL, 0 },
+ 0,
+ 0,
+ (HANDLE) NULL,
+ (HANDLE) NULL,
+ (HANDLE) NULL,
+#ifdef HAVE_CAST_TO_UNION
+ (LARGE_INTEGER) (Tcl_WideInt) 0,
+ (ULARGE_INTEGER) (DWORDLONG) 0,
+ (LARGE_INTEGER) (Tcl_WideInt) 0,
+ (LARGE_INTEGER) (Tcl_WideInt) 0,
+#else
+ 0,
+ 0,
+ 0,
+ 0,
+#endif
+ { 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 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);
-static DWORD WINAPI CalibrationThread _ANSI_ARGS_(( LPVOID arg ));
+/*
+ * TIP #233 (Virtualized Time): Data for the time hooks, if any.
+ */
-static void UpdateTimeEachSecond _ANSI_ARGS_(( void ));
+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.
@@ -128,10 +152,11 @@ static void UpdateTimeEachSecond _ANSI_ARGS_(( void ));
*/
unsigned long
-TclpGetSeconds()
+TclpGetSeconds(void)
{
Tcl_Time t;
- TclpGetTime( &t );
+
+ tclGetTimeProcPtr(&t, tclTimeClientData); /* Tcl_GetTime inlined. */
return t.sec;
}
@@ -140,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.
@@ -156,18 +180,19 @@ TclpGetSeconds()
*/
unsigned long
-TclpGetClicks()
+TclpGetClicks(void)
{
/*
- * Use the TclpGetTime 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 */
- TclpGetTime( &now );
- retval = ( now.sec * 1000000 ) + now.usec;
+ tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */
+
+ retval = (now.sec * 1000000) + now.usec;
return retval;
}
@@ -175,169 +200,254 @@ TclpGetClicks()
/*
*----------------------------------------------------------------------
*
- * TclpGetTimeZone --
+ * Tcl_GetTime --
*
- * Determines the current timezone. The method varies wildly
- * between different Platform implementations, so its hidden in
- * this function.
+ * Gets the current system time in seconds and microseconds since the
+ * beginning of the epoch: 00:00 UCT, January 1, 1970.
*
* Results:
- * Minutes west of GMT.
+ * Returns the current time in timePtr.
*
* Side effects:
- * None.
+ * 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.
*
*----------------------------------------------------------------------
*/
-int
-TclpGetTimeZone (currentTime)
- unsigned long currentTime;
+void
+Tcl_GetTime(
+ Tcl_Time *timePtr) /* Location to store time information. */
{
- int timeZone;
-
- tzset();
- timeZone = _timezone / 60;
+ 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.
+ *
+ *----------------------------------------------------------------------
+ */
- return timeZone;
+static void
+NativeScaleTime(
+ Tcl_Time *timePtr,
+ ClientData clientData)
+{
+ /*
+ * Native scale is 1:1. Nothing is done.
+ */
}
/*
*----------------------------------------------------------------------
*
- * TclpGetTime --
+ * NativeGetTime --
*
- * Gets the current system time in seconds and microseconds
+ * 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.
+ * 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
-TclpGetTime(timePtr)
- Tcl_Time *timePtr; /* Location to store time information. */
+static void
+NativeGetTime(
+ Tcl_Time *timePtr,
+ ClientData clientData)
{
-
- struct timeb t;
-
- /* Initialize static storage on the first trip through. */
+ 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.curCounterFreq );
+ 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. If it's anything else, we
- * presume that the performance counter is unreliable.
+ * 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 ( timeInfo.perfCounterAvailable
- && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 1193182
- && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 3579545 ) {
- timeInfo.perfCounterAvailable = FALSE;
+#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 ( timeInfo.perfCounterAvailable ) {
+ 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 );
+
+ 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
+ */
+
+ WaitForSingleObject(timeInfo.readyEvent, INFINITE);
+ CloseHandle(timeInfo.readyEvent);
+ Tcl_CreateExitHandler(StopCalibration, 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 */
-
- LONGLONG 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 */
-
- LONGLONG usecSincePosixEpoch;
- /* Current microseconds since Posix epoch */
+ /* Posix epoch expressed as 100-ns ticks since
+ * the windows epoch. */
+ Tcl_WideInt usecSincePosixEpoch;
+ /* Current microseconds since Posix epoch. */
posixEpoch.LowPart = 0xD53E8000;
posixEpoch.HighPart = 0x019DB1DE;
- 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;
+ EnterCriticalSection(&timeInfo.cs);
+
+ 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.
+ */
+
+ 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;
+ }
+
+ LeaveCriticalSection(&timeInfo.cs);
+ }
+
+ if (useFtime) {
+ /*
+ * High resolution timer is not available. Just use ftime.
+ */
+
+ _ftime(&t);
+ timePtr->sec = (long)t.time;
timePtr->usec = t.millitm * 1000;
}
}
@@ -345,87 +455,35 @@ TclpGetTime(timePtr)
/*
*----------------------------------------------------------------------
*
- * TclpGetTZName --
+ * StopCalibration --
*
- * Gets the current timezone string.
+ * Turns off the calibration thread in preparation for exiting the
+ * process.
*
* Results:
- * Returns a pointer to a static string, or NULL on failure.
+ * None.
*
* Side effects:
- * None.
+ * Sets the 'exitEvent' event in the 'timeInfo' structure to ask the
+ * thread in question to exit, and waits for it to do so.
*
*----------------------------------------------------------------------
*/
-char *
-TclpGetTZName(int dst)
+static void
+StopCalibration(
+ ClientData unused) /* Client data is unused */
{
- int len;
- char *zone, *p;
- TIME_ZONE_INFORMATION tz;
- Tcl_Encoding encoding;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- char *name = tsdPtr->tzName;
+ SetEvent(timeInfo.exitEvent);
/*
- * 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.
+ * If Tcl_Finalize was called from DllMain, the calibration thread is in a
+ * paused state so we need to timeout and continue.
*/
- 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;
+ WaitForSingleObject(timeInfo.calibrationThread, 100);
+ CloseHandle(timeInfo.exitEvent);
+ CloseHandle(timeInfo.calibrationThread);
}
/*
@@ -433,9 +491,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.
@@ -447,40 +505,55 @@ 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;
- long time;
+ time_t time;
if (!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();
@@ -489,13 +562,13 @@ 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;
time -= 60;
}
-
+
time = tmPtr->tm_min + time/60;
tmPtr->tm_min = (int)(time % 60);
if (tmPtr->tm_min < 0) {
@@ -511,12 +584,12 @@ TclpGetDate(t, useGMT)
}
time /= 24;
- tmPtr->tm_mday += time;
- tmPtr->tm_yday += time;
- tmPtr->tm_wday = (tmPtr->tm_wday + time) % 7;
+ tmPtr->tm_mday += (int)time;
+ tmPtr->tm_yday += (int)time;
+ tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7;
}
} else {
- tmPtr = ComputeGMT(tp);
+ tmPtr = ComputeGMT(t);
}
return tmPtr;
}
@@ -526,8 +599,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.
@@ -539,13 +612,13 @@ TclpGetDate(t, useGMT)
*/
static struct tm *
-ComputeGMT(tp)
- const time_t *tp;
+ComputeGMT(
+ const time_t *tp)
{
struct tm *tmPtr;
long tmp, rem;
int isLeap;
- int *days;
+ const int *days;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
tmPtr = &tsdPtr->tm;
@@ -554,8 +627,8 @@ ComputeGMT(tp)
* Compute the 4 year span containing the specified time.
*/
- tmp = *tp / SECSPER4YEAR;
- rem = *tp % SECSPER4YEAR;
+ tmp = (long)(*tp / SECSPER4YEAR);
+ rem = (long)(*tp % SECSPER4YEAR);
/*
* Correct for weird mod semantics so the remainder is always positive.
@@ -567,9 +640,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;
@@ -591,13 +664,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.
*/
@@ -613,6 +686,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];
@@ -621,7 +695,7 @@ ComputeGMT(tp)
* Compute day of week. Epoch started on a Thursday.
*/
- tmPtr->tm_wday = (*tp / SECSPERDAY) + 4;
+ tmPtr->tm_wday = (long)(*tp / SECSPERDAY) + 4;
if ((*tp % SECSPERDAY) < 0) {
tmPtr->tm_wday--;
}
@@ -638,57 +712,68 @@ 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 clockCalibrateWakeupInterval ms, 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 */
-
- GetSystemTimeAsFileTime( &curFileTime );
- QueryPerformanceCounter( &timeInfo.lastCounter );
- QueryPerformanceFrequency( &timeInfo.curCounterFreq );
- timeInfo.lastFileTime.LowPart = curFileTime.dwLowDateTime;
- timeInfo.lastFileTime.HighPart = curFileTime.dwHighDateTime;
+ /*
+ * Get initial system time and performance counter.
+ */
- /* Initialize the working storage for the calibration callback */
+ GetSystemTimeAsFileTime(&curFileTime);
+ QueryPerformanceCounter(&timeInfo.perfCounterLastCall);
+ QueryPerformanceFrequency(&timeInfo.curCounterFreq);
+ timeInfo.fileTimeLastCall.LowPart = curFileTime.dwLowDateTime;
+ timeInfo.fileTimeLastCall.HighPart = curFileTime.dwHighDateTime;
- timeInfo.lastPerfCounter = timeInfo.lastCounter.QuadPart;
- timeInfo.estPerfCounterFreq = 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.
+ */
- /* Run the calibration once a second */
+ while (timeInfo.perfCounterAvailable) {
+ /*
+ * If the exitEvent is set, break out of the loop.
+ */
- for ( ; ; ) {
- Sleep( 1000 );
+ waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE);
+ if (waitResult == WAIT_OBJECT_0) {
+ break;
+ }
UpdateTimeEachSecond();
}
@@ -701,11 +786,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.
@@ -717,131 +802,364 @@ CalibrationThread( LPVOID arg )
*/
static void
-UpdateTimeEachSecond()
+UpdateTimeEachSecond(void)
{
-
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 */
+ * 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. */
/*
- * Get current performance counter and system time.
+ * 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);
/*
- * 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.
+ * 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.
*/
- perfCounterDiff = curPerfCounter.QuadPart - timeInfo.lastPerfCounter;
- timeInfo.lastPerfCounter = curPerfCounter.QuadPart;
- fileTimeDiff = curFileTime.QuadPart - timeInfo.lastSysTime;
- timeInfo.lastSysTime = curFileTime.QuadPart;
- instantFreq = ( 10000000 * perfCounterDiff / fileTimeDiff );
+ if (timeInfo.curCounterFreq.QuadPart == 0){
+ LeaveCriticalSection(&timeInfo.cs);
+ timeInfo.perfCounterAvailable = 0;
+ return;
+ }
/*
- * Consider this a timing glitch if instant frequency varies
- * significantly from the current estimate.
+ * 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.
*/
- fuzz = timeInfo.estPerfCounterFreq >> 10;
- lowBound = timeInfo.estPerfCounterFreq - fuzz;
- hiBound = timeInfo.estPerfCounterFreq + fuzz;
- if ( instantFreq < lowBound || instantFreq > hiBound ) {
- LeaveCriticalSection( &timeInfo.cs );
- return;
- }
+ estFreq = AccumulateSample(curPerfCounter.QuadPart,
+ (Tcl_WideUInt) curFileTime.QuadPart);
/*
- * Update the current estimate of performance counter frequency.
- * This code is equivalent to the loop filter of a phase locked
- * loop.
+ * We want to adjust things so that time appears to be continuous.
+ * Virtual file time, right now, is
+ *
+ * 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
+ *
+ * vt1 = 20000000 + curFileTime
+ *
+ * The frequency that we need to use to drift the counter back into place
+ * is estFreq * 20000000 / (vt1 - vt0)
*/
- delta = ( instantFreq - timeInfo.estPerfCounterFreq ) >> 6;
- timeInfo.estPerfCounterFreq += delta;
+ vt0 = 10000000 * (curPerfCounter.QuadPart
+ - timeInfo.perfCounterLastCall.QuadPart)
+ / timeInfo.curCounterFreq.QuadPart
+ + timeInfo.fileTimeLastCall.QuadPart;
+ vt1 = 20000000 + curFileTime.QuadPart;
/*
- * Update the current virtual time.
+ * 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.
*/
- timeInfo.lastFileTime.QuadPart
- += ( ( curPerfCounter.QuadPart - timeInfo.lastCounter.QuadPart )
- * 10000000 / timeInfo.curCounterFreq.QuadPart );
- timeInfo.lastCounter.QuadPart = curPerfCounter.QuadPart;
+ tdiff = vt0 - curFileTime.QuadPart;
+ if (tdiff > 10000000 || tdiff < -10000000) {
+ timeInfo.fileTimeLastCall.QuadPart = curFileTime.QuadPart;
+ timeInfo.curCounterFreq.QuadPart = estFreq;
+ } else {
+ driftFreq = estFreq * 20000000 / (vt1 - vt0);
- delta = curFileTime.QuadPart - timeInfo.lastFileTime.QuadPart;
- if ( delta > 10000000 || delta < -10000000 ) {
+ if (driftFreq > 1003*estFreq/1000) {
+ driftFreq = 1003*estFreq/1000;
+ } else if (driftFreq < 997*estFreq/1000) {
+ driftFreq = 997*estFreq/1000;
+ }
- /*
- * 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.fileTimeLastCall.QuadPart = vt0;
+ timeInfo.curCounterFreq.QuadPart = driftFreq;
+ }
- timeInfo.lastFileTime.QuadPart = curFileTime.QuadPart;
- timeInfo.curCounterFreq.QuadPart = timeInfo.estPerfCounterFreq;
+ timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart;
+ LeaveCriticalSection(&timeInfo.cs);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ResetCounterSamples --
+ *
+ * Fills the sample arrays in 'timeInfo' with dummy values that will
+ * yield the current performance counter and frequency.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The array of samples is filled in so that it appears that there are
+ * SAMPLES samples at one-second intervals, separated by precisely the
+ * given frequency.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ResetCounterSamples(
+ Tcl_WideUInt fileTime, /* Current file time */
+ Tcl_WideInt perfCounter, /* Current performance counter */
+ Tcl_WideInt perfFreq) /* Target performance frequency */
+{
+ int i;
+ for (i=SAMPLES-1 ; i>=0 ; --i) {
+ timeInfo.perfCounterSample[i] = perfCounter;
+ timeInfo.fileTimeSample[i] = fileTime;
+ perfCounter -= perfFreq;
+ fileTime -= 10000000;
+ }
+ timeInfo.sampleNo = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AccumulateSample --
+ *
+ * Updates the circular buffer of performance counter and system time
+ * samples with a new data point.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The new data point replaces the oldest point in the circular buffer,
+ * and the descriptive statistics are updated to accumulate the new
+ * point.
+ *
+ * Several things may have gone wrong here that have to be checked for.
+ * (1) The performance counter may have jumped.
+ * (2) The system clock may have been reset.
+ *
+ * In either case, we'll need to reinitialize the circular buffer with samples
+ * relative to the current system time and the NOMINAL performance frequency
+ * (not the actual, because the actual has probably run slow in the first
+ * case).
+ */
+
+static Tcl_WideInt
+AccumulateSample(
+ Tcl_WideInt perfCounter,
+ Tcl_WideUInt fileTime)
+{
+ Tcl_WideUInt workFTSample; /* File time sample being removed from or
+ * added to the circular buffer. */
+ Tcl_WideInt workPCSample; /* Performance counter sample being removed
+ * from or added to the circular buffer. */
+ Tcl_WideUInt lastFTSample; /* Last file time sample recorded */
+ Tcl_WideInt lastPCSample; /* Last performance counter sample recorded */
+ Tcl_WideInt FTdiff; /* Difference between last FT and current */
+ Tcl_WideInt PCdiff; /* Difference between last PC and current */
+ Tcl_WideInt estFreq; /* Estimated performance counter frequency */
+
+ /*
+ * Test for jumps and reset the samples if we have one.
+ */
+
+ if (timeInfo.sampleNo == 0) {
+ lastPCSample =
+ timeInfo.perfCounterSample[timeInfo.sampleNo + SAMPLES - 1];
+ lastFTSample =
+ timeInfo.fileTimeSample[timeInfo.sampleNo + SAMPLES - 1];
} else {
+ lastPCSample = timeInfo.perfCounterSample[timeInfo.sampleNo - 1];
+ lastFTSample = timeInfo.fileTimeSample[timeInfo.sampleNo - 1];
+ }
+ PCdiff = perfCounter - lastPCSample;
+ FTdiff = fileTime - lastFTSample;
+ if (PCdiff < timeInfo.nominalFreq.QuadPart * 9 / 10
+ || PCdiff > timeInfo.nominalFreq.QuadPart * 11 / 10
+ || FTdiff < 9000000 || FTdiff > 11000000) {
+ ResetCounterSamples(fileTime, perfCounter,
+ timeInfo.nominalFreq.QuadPart);
+ return timeInfo.nominalFreq.QuadPart;
+ } 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.
+ * Estimate the frequency.
*/
-
- timeInfo.curCounterFreq.QuadPart
- = 10000000 * timeInfo.estPerfCounterFreq / ( delta + 10000000 );
+
+ 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;
/*
- * Limit frequency excursions to 1000 ppm from estimate
+ * Advance the sample number.
*/
-
- if ( timeInfo.curCounterFreq.QuadPart < lowBound ) {
- timeInfo.curCounterFreq.QuadPart = lowBound;
- } else if ( timeInfo.curCounterFreq.QuadPart > hiBound ) {
- timeInfo.curCounterFreq.QuadPart = hiBound;
+
+ if (++timeInfo.sampleNo >= SAMPLES) {
+ timeInfo.sampleNo = 0;
}
+
+ return estFreq;
}
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpGmtime --
+ *
+ * Wrapper around the 'gmtime' library function to make it thread safe.
+ *
+ * Results:
+ * Returns a pointer to a 'struct tm' in thread-specific data.
+ *
+ * Side effects:
+ * Invokes gmtime or gmtime_r as appropriate.
+ *
+ *----------------------------------------------------------------------
+ */
+
+struct tm *
+TclpGmtime(
+ const time_t *timePtr) /* 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.
+ */
- LeaveCriticalSection( &timeInfo.cs );
+ return gmtime(timePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpLocaltime --
+ *
+ * Wrapper around the 'localtime' library function to make it thread
+ * safe.
+ *
+ * Results:
+ * Returns a pointer to a 'struct tm' in thread-specific data.
+ *
+ * Side effects:
+ * Invokes localtime or localtime_r as appropriate.
+ *
+ *----------------------------------------------------------------------
+ */
+
+struct tm *
+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.
+ */
+ 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:
+ */