/* * tclWinTime.c -- * * 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. */ #include "tclInt.h" #define SECSPERDAY (60L * 60L * 24L) #define SECSPERYEAR (SECSPERDAY * 365L) #define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY) /* * The following arrays contain the day of year for the last day of each * month, where index 1 is January. */ static const int normalDays[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }; static const int leapDays[] = { -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; typedef struct ThreadSpecificData { char tzName[64]; /* Time zone name */ struct tm tm; /* time information */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; /* * The following structure used for calculating virtual time. Virtual * time is always equal to: * virtTimeBase + (currentPerfCounter - perfCounter) * * 10000000 / nominalFreq */ typedef struct TimeCalibInfo { LONGLONG perfCounter; /* QPC value of last calibrated virtual time */ Tcl_WideInt virtTimeBase; /* Last virtual time base (in 100-ns) */ Tcl_WideInt monoTimeBase; /* Last monotonic time base (in 100-ns) */ Tcl_WideInt sysTime; /* Last real system time (in 100-ns), truncated to VT_SYSTMR_DIST (100ms) */ } TimeCalibInfo; /* Milliseconds <-> 100-ns ticks */ #define MsToT100ns(ms) ((ms) * 10000) #define T100nsToMs(ms) ((ms) / 10000) /* Microseconds <-> 100-ns ticks */ #define UsToT100ns(ms) ((ms) * 10) #define T100nsToUs(ms) ((ms) / 10) /* * Use factor 1000 for the frequencies of QPC if it ascertained in Hz: * frequency = nominal frequency / 1000 * native perf-counter = original perf-counter * 1000 */ #ifndef TCL_VT_FREQ_FACTOR # define TCL_VT_FREQ_FACTOR 1 #endif /* Distance in ms to obtain system timer (avoids unneeded syscalls). */ #define VT_SYSTMR_MIN_DIST 50 /* Resolution distance of the system-timer in milliseconds, * should be greater as real resolution (normally 15.6ms) to make more * accurate approximated part of virtual time */ #define VT_SYSTMR_DIST 250 /* Max discrepancy of virtual time to system time. Time can slow drift * to the drift distance (+/-5ms), if reached this distance relative * current system time. * Note: it should be greater as real timer-resolution (> 15.6ms). */ #define VT_MAX_DISCREPANCY 20 /* Max virtual time drift to shorten current distance */ #define VT_MAX_DRIFT_TIME 4 /* * Data for managing high-resolution timers (virtual time). */ 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. */ LONGLONG nominalFreq; /* Nominal frequency of the system performance * counter, that is, the value returned from * QueryPerformanceFrequency. */ #if TCL_VT_FREQ_FACTOR int freqFactor; /* Frequency factor (1 - KHz, 1000 - Hz) */ #endif LARGE_INTEGER posixEpoch; /* Posix epoch expressed as 100-ns ticks since * the windows epoch. */ TimeCalibInfo lastCI; /* Last virtual timer-data updated in the * calibration process. */ volatile LONG lastCIEpoch; /* Calibration epoch (increased each 100ms) */ size_t lastUsedTime; /* Last known (caller) offset to time base * (used to avoid back-drifts after calibrate) */ } TimeInfo; static TimeInfo timeInfo = { { NULL, 0, 0, NULL, NULL, 0 }, 0, 0, (LONGLONG) 0, #if TCL_VT_FREQ_FACTOR 1, /* for frequency in KHz */ #endif #ifdef HAVE_CAST_TO_UNION (LARGE_INTEGER) (Tcl_WideInt) 0, #else {0, 0}, #endif { (LONGLONG) 0, (Tcl_WideInt) 0, (Tcl_WideInt) 0, }, (LONG) 0, (Tcl_WideInt) 0 }; /* * 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}; /* * Declarations for functions defined later in this file. */ static struct tm * ComputeGMT(const time_t *tp); static void NativeScaleTime(Tcl_Time* timebuf, ClientData clientData); static Tcl_WideInt NativeGetMicroseconds(int monotonic); 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; /* *---------------------------------------------------------------------- * * NativePerformanceCounter -- * * Used instead of QueryPerformanceCounter to consider frequency factor. * * Results: * Returns QPC corresponding current frequency factor. * *---------------------------------------------------------------------- */ static inline LONGLONG NativePerformanceCounter(void) { LARGE_INTEGER curCounter; QueryPerformanceCounter(&curCounter); #if TCL_VT_FREQ_FACTOR if (timeInfo.freqFactor == 1) { return curCounter.QuadPart; /* no factor */ } /* defactoring counter */ return curCounter.QuadPart / timeInfo.freqFactor; #else return curCounter.QuadPart; /* no factor configured */ #endif } /* *---------------------------------------------------------------------- * * NativeCalc100NsOffs -- * * Calculate the current system time in 100-ns ticks since some base, * for current performance counter (curCounter), using given calibrated values. * * offs = (curCounter - lastCI.perfCounter) * 10000000 / nominalFreq * * vt = lastCI.virtTimeBase + offs * mt = lastCI.monoTimeBase + offs * * Results: * Returns the wide integer with number of 100-ns ticks from the epoch. * * Side effects: * None * *---------------------------------------------------------------------- */ static inline Tcl_WideInt NativeCalc100NsOffs( LONGLONG ciPerfCounter, LONGLONG curCounter ) { curCounter -= ciPerfCounter; /* current distance */ if (!curCounter) { return 0; /* virtual time without offset */ } /* virtual time with offset */ return curCounter * 10000000 / timeInfo.nominalFreq; } /* * Representing the number of 100-nanosecond intervals since posix epoch. */ static inline Tcl_WideInt GetSystemTimeAsVirtual(void) { FILETIME curSysTime; /* Current system time. */ LARGE_INTEGER curFileTime; /* 100-ns ticks since since Jan 1, 1601 (UTC) */ GetSystemTimeAsFileTime(&curSysTime); curFileTime.LowPart = curSysTime.dwLowDateTime; curFileTime.HighPart = curSysTime.dwHighDateTime; return (Tcl_WideInt)(curFileTime.QuadPart - timeInfo.posixEpoch.QuadPart); } /* *---------------------------------------------------------------------- * * TclpGetSeconds -- * * 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. * * Side effects: * None. * *---------------------------------------------------------------------- */ unsigned long TclpGetSeconds(void) { /* Try to use high resolution timer */ if (tclGetTimeProcPtr == NativeGetTime) { return NativeGetMicroseconds(0) / 1000000; } else { Tcl_Time t; tclGetTimeProcPtr(&t, tclTimeClientData); /* Tcl_GetTime inlined. */ return t.sec; } } /* *---------------------------------------------------------------------- * * 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. * * Results: * Number of clicks from some start time. * * Side effects: * None. * *---------------------------------------------------------------------- */ unsigned long TclpGetClicks(void) { /* Try to use high resolution timer */ if (tclGetTimeProcPtr == NativeGetTime) { return (unsigned long)NativeGetMicroseconds(1); } 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 */ tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */ return (unsigned long)(now.sec * 1000000) + 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). * *---------------------------------------------------------------------- */ Tcl_WideInt 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 (Tcl_WideInt)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. * *---------------------------------------------------------------------- */ double TclpWideClickInMicrosec(void) { if (!wideClick.initialized) { (void)TclpGetWideClicks(); /* initialize */ } return wideClick.microsecsScale; } /* *---------------------------------------------------------------------- * * TclpGetMicroseconds -- * * This procedure returns a WideInt value that represents the highest * resolution clock in microseconds available on the system. * * Results: * Number of microseconds (from the epoch). * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_WideInt TclpGetMicroseconds(void) { /* Use high resolution timer if possible */ if (tclGetTimeProcPtr == NativeGetTime) { return NativeGetMicroseconds(0); } else { /* * Use the Tcl_GetTime abstraction to get the time in microseconds, as * nearly as we can, and return it. */ Tcl_Time now; tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */ return TCL_TIME_TO_USEC(now); } } /* *---------------------------------------------------------------------- * * TclpGetMicroseconds -- * * This procedure returns a WideInt value that represents the highest * resolution clock in microseconds available on the system. * * Results: * Number of microseconds (from the epoch). * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_WideInt TclpGetUTimeMonotonic(void) { /* Use high resolution timer if possible */ if (tclGetTimeProcPtr == NativeGetTime) { return NativeGetMicroseconds(1); /* monotonic based time */ } else { /* * Use the Tcl_GetTime abstraction to get the time in microseconds, as * nearly as we can, and return it. */ Tcl_Time now; tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */ return TCL_TIME_TO_USEC(now); } } /* *---------------------------------------------------------------------- * * Tcl_GetTime -- * * 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. * *---------------------------------------------------------------------- */ void Tcl_GetTime( Tcl_Time *timePtr) /* Location to store time information. */ { /* Try to use high resolution timer */ if ( tclGetTimeProcPtr == NativeGetTime) { Tcl_WideInt now = NativeGetMicroseconds(0); timePtr->sec = (long) (now / 1000000); timePtr->usec = (unsigned long) (now % 1000000); } else { 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. * *---------------------------------------------------------------------- */ static void NativeScaleTime( Tcl_Time *timePtr, ClientData clientData) { /* * Native scale is 1:1. Nothing is done. */ } /* *---------------------------------------------------------------------- * * TclpScaleUTime -- * * This procedure scales number of microseconds if expected. * * Results: * Number of microseconds scaled using tclScaleTimeProcPtr. * *---------------------------------------------------------------------- */ void TclpScaleUTime( Tcl_WideInt *usec) { /* Native scale is 1:1. */ if (tclScaleTimeProcPtr != NativeScaleTime) { return; } else { Tcl_Time scTime; scTime.sec = *usec / 1000000; scTime.usec = *usec % 1000000; tclScaleTimeProcPtr(&scTime, tclTimeClientData); *usec = ((Tcl_WideInt)scTime.sec) * 1000000 + scTime.usec; } } /* *---------------------------------------------------------------------- * * NativeGetMicroseconds -- * * Gets the current system time in 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. * * 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. * *---------------------------------------------------------------------- */ static Tcl_WideInt NativeGetMicroseconds( int monotonic) { static size_t nomObtainSTPerfCntrDist = 0; /* Nominal distance in perf-counter ticks to * obtain system timer (avoids unneeded syscalls). */ Tcl_WideInt curTime; /* Current time in 100-ns ticks since epoch */ /* * 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) { LARGE_INTEGER nominalFreq; TclpInitLock(); if (!timeInfo.initialized) { timeInfo.posixEpoch.LowPart = 0xD53E8000; timeInfo.posixEpoch.HighPart = 0x019DB1DE; if ((timeInfo.perfCounterAvailable = QueryPerformanceFrequency(&nominalFreq)) ) { timeInfo.nominalFreq = nominalFreq.QuadPart; /* * We devide by timeInfo.nominalFreq in several places. */ if (timeInfo.nominalFreq == 0) { timeInfo.perfCounterAvailable = FALSE; } #if TCL_VT_FREQ_FACTOR /* Some systems having frequency in Hz, so save the factor here */ if (timeInfo.nominalFreq >= 1000000000 && (timeInfo.nominalFreq % 1000) == 0) { /* assume that frequency in Hz, factor used only for tolerance */ timeInfo.freqFactor = 1000; timeInfo.nominalFreq /= timeInfo.freqFactor; } #endif /* Distance in perf-counter ticks for VT_SYSTMR_MIN_DIST (ms) */ nomObtainSTPerfCntrDist = (size_t) (timeInfo.nominalFreq * MsToT100ns(VT_SYSTMR_MIN_DIST)) / 10000000; } /* * 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 != 1193182 * && timeInfo.nominalFreq != 3579545 */ && timeInfo.nominalFreq > 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) == 0xF00) /* Pentium 4 */ || ((regs[0]&0x00000F00) == 0x600) ) /* or compatible (VM) */ && ((regs[0] & 0x0FF00000) /* Extended family (bits 20-27) */ || (regs[3] & 0x10000000))) /* Hyperthread (bit 28) */ || (((regs[1]&0x00FF0000) >> 16) >= 2 /* CPU count */ || systemInfo.dwNumberOfProcessors >= 2)) { timeInfo.perfCounterAvailable = TRUE; } else { timeInfo.perfCounterAvailable = FALSE; } } #endif /* above code is Win32 only */ /* * If the performance counter is available, initialize */ if (timeInfo.perfCounterAvailable) { InitializeCriticalSection(&timeInfo.cs); timeInfo.lastCI.perfCounter = NativePerformanceCounter(); /* base of the real-time (and last known system time) */ timeInfo.lastCI.sysTime = timeInfo.lastCI.virtTimeBase = GetSystemTimeAsVirtual(); /* base of the monotonic time */ timeInfo.lastCI.monoTimeBase = NativeCalc100NsOffs( 0, timeInfo.lastCI.perfCounter); } timeInfo.initialized = TRUE; } TclpInitUnlock(); } if (timeInfo.perfCounterAvailable) { static LONGLONG lastObtainSTPerfCntr = 0; /* Last perf-counter system timer was obtained. */ TimeCalibInfo ci; /* Copy of common base/offset used to calc VT. */ volatile LONG ciEpoch; /* Epoch of "ci", protecting this structure. */ Tcl_WideInt sysTime, trSysTime; /* System time and truncated (rounded) time. */ LONGLONG curCounter; /* Current value of native QPC. */ /* * Try to acquire data without lock (same epoch at end of copy process). */ ciEpoch = timeInfo.lastCIEpoch; memcpy(&ci, &timeInfo.lastCI, sizeof(ci)); /* * Lock on demand and hold time section locked as short as possible. */ if (InterlockedCompareExchange(&timeInfo.lastCIEpoch, ciEpoch, ciEpoch) != ciEpoch) { EnterCriticalSection(&timeInfo.cs); if (ciEpoch != timeInfo.lastCIEpoch) { memcpy(&ci, &timeInfo.lastCI, sizeof(ci)); ciEpoch = timeInfo.lastCIEpoch; } LeaveCriticalSection(&timeInfo.cs); } /* Query current performance counter. */ curCounter = NativePerformanceCounter(); /* Avoid doing unneeded syscall too often */ if ( curCounter >= lastObtainSTPerfCntr && curCounter < lastObtainSTPerfCntr + nomObtainSTPerfCntrDist ) { goto calcVT; /* don't check system time (curCounter precise enough) */ } lastObtainSTPerfCntr = curCounter; /* Query non-precise system time */ sysTime = GetSystemTimeAsVirtual(); /* * Truncate non-precise part of the system time (to VT_SYSTMR_DIST ms) */ trSysTime = sysTime; trSysTime /= MsToT100ns(VT_SYSTMR_DIST); /* VT_SYSTMR_DIST ms (in 100ns)*/ trSysTime *= MsToT100ns(VT_SYSTMR_DIST); /* * If rounded system time is changed - recalibrate offsets/base values */ if (ci.sysTime != trSysTime) { /* next interval VT_SYSTMR_DIST ms */ EnterCriticalSection(&timeInfo.cs); if (ci.sysTime != trSysTime) { /* again in lock (done in other thread) */ /* * Recalibration / Adjustment of base values. */ Tcl_WideInt vt0, vt1; /* Desired virtual time */ Tcl_WideInt tdiff; /* Time difference to the system time */ Tcl_WideInt lastTime; /* Used to compare with last known time */ /* New desired virtual time using current base values */ vt1 = vt0 = ci.virtTimeBase + NativeCalc100NsOffs(ci.perfCounter, curCounter); tdiff = vt0 - sysTime; /* If we can adjust offsets (not a jump to new system time) */ if (MsToT100ns(-800) < tdiff && tdiff < MsToT100ns(800)) { /* Allow small drift if discrepancy larger as expected */ if (tdiff <= MsToT100ns(-VT_MAX_DISCREPANCY)) { vt0 += MsToT100ns(VT_MAX_DRIFT_TIME); } else if (tdiff <= MsToT100ns(-VT_MAX_DRIFT_TIME)) { vt0 -= tdiff / 2; /* small drift forwards */ } else if (tdiff >= MsToT100ns(VT_MAX_DISCREPANCY)) { vt0 -= MsToT100ns(VT_MAX_DRIFT_TIME); } /* * Be sure the clock ticks never backwards (avoid backwards * time-drifts). If time-reset (< 800ms) just use curent time * (avoid time correction in such case). */ if ( (lastTime = (ci.virtTimeBase + timeInfo.lastUsedTime)) && (lastTime -= vt0) > 0 /* offset to vt0 */ && lastTime < MsToT100ns(800) /* bypass time-switch (drifts only) */ ) { vt0 += lastTime; /* hold on the time a bit */ } /* difference for addjustment of monotonic base */ tdiff = vt0 - vt1; } else { /* * The time-jump (reset or initial), we should use system time * instead of virtual to recalibrate offsets (let the time jump). */ vt0 = sysTime; tdiff = 0; } /* * Now adjust monotonic time base, note this time should absolutely * never ticks backwards (relative the last known monotonic time). */ ci.monoTimeBase += NativeCalc100NsOffs(ci.perfCounter, curCounter); ci.monoTimeBase += tdiff; lastTime = (timeInfo.lastCI.monoTimeBase + timeInfo.lastUsedTime); if (ci.monoTimeBase < lastTime) { ci.monoTimeBase = lastTime; /* freeze monotonic time a bit */ } /* * Adjustment of current base for virtual time. This will also * prevent too large counter difference (resp. max distance ~ 100ms). */ ci.virtTimeBase = vt0; ci.perfCounter = curCounter; ci.sysTime = trSysTime; /* base adjusted, so reset also last known offset */ timeInfo.lastUsedTime = 0; /* Update global structure lastCI with new values */ memcpy(&timeInfo.lastCI, &ci, sizeof(ci)); /* Increase epoch, to inform all other threads about new data */ InterlockedIncrement(&timeInfo.lastCIEpoch); } /* end lock */ LeaveCriticalSection(&timeInfo.cs); } /* common info lastCI contains actual data */ calcVT: /* Calculate actual time-offset using performance counter */ curTime = NativeCalc100NsOffs(ci.perfCounter, curCounter); /* Save last used time (offset) */ timeInfo.lastUsedTime = (size_t)curTime; if (monotonic) { /* Use monotonic time base */ curTime += ci.monoTimeBase; } else { /* Use real-time base */ curTime += ci.virtTimeBase; } /* Return virtual time */ return T100nsToUs(curTime); /* 100-ns to microseconds */ } /* * High resolution timer is not available. */ curTime = GetSystemTimeAsVirtual(); /* in 100-ns ticks */ return T100nsToUs(curTime); /* 100-ns to microseconds */ } /* *---------------------------------------------------------------------- * * 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, ClientData clientData) { Tcl_WideInt now; now = NativeGetMicroseconds(0); timePtr->sec = (long) (now / 1000000); timePtr->usec = (unsigned long) (now % 1000000); } /* *---------------------------------------------------------------------- * * 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. * * Results: * Returns a static tm structure. * * Side effects: * None. * *---------------------------------------------------------------------- */ struct tm * TclpGetDate( const time_t *t, int useGMT) { struct tm *tmPtr; 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. */ /* * 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 = *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. */ if (*t < (LONG_MAX - 2*SECSPERDAY) && *t > (LONG_MIN + 2*SECSPERDAY)) { tmPtr = ComputeGMT(&time); } else { tmPtr = ComputeGMT(t); tzset(); /* * Add the bias directly to the tm structure to avoid overflow. * Propagate seconds overflow into minutes, hours and days. */ 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) { tmPtr->tm_min += 60; time -= 60; } time = tmPtr->tm_hour + time/60; tmPtr->tm_hour = (int)(time % 24); if (tmPtr->tm_hour < 0) { tmPtr->tm_hour += 24; time -= 24; } time /= 24; tmPtr->tm_mday += (int)time; tmPtr->tm_yday += (int)time; tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7; } } else { tmPtr = ComputeGMT(t); } return tmPtr; } /* *---------------------------------------------------------------------- * * ComputeGMT -- * * 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. * * Side effects: * Updates the values of the static struct tm. * *---------------------------------------------------------------------- */ static struct tm * ComputeGMT( const time_t *tp) { struct tm *tmPtr; long tmp, rem; int isLeap; const int *days; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); tmPtr = &tsdPtr->tm; /* * Compute the 4 year span containing the specified time. */ tmp = (long)(*tp / SECSPER4YEAR); rem = (long)(*tp % SECSPER4YEAR); /* * Correct for weird mod semantics so the remainder is always positive. */ if (rem < 0) { tmp--; rem += SECSPER4YEAR; } /* * 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; isLeap = 0; if (rem >= SECSPERYEAR) { /* 1971, etc. */ tmp++; rem -= SECSPERYEAR; if (rem >= SECSPERYEAR) { /* 1972, etc. */ tmp++; rem -= SECSPERYEAR; if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */ tmp++; rem -= SECSPERYEAR + SECSPERDAY; } else { isLeap = 1; } } } tmPtr->tm_year = tmp; /* * 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. */ tmPtr->tm_hour = rem / 3600; rem %= 3600; tmPtr->tm_min = rem / 60; tmPtr->tm_sec = rem % 60; /* * Compute the month and day of month. */ 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]; /* * Compute day of week. Epoch started on a Thursday. */ tmPtr->tm_wday = (long)(*tp / SECSPERDAY) + 4; if ((*tp % SECSPERDAY) < 0) { tmPtr->tm_wday--; } tmPtr->tm_wday %= 7; if (tmPtr->tm_wday < 0) { tmPtr->tm_wday += 7; } return tmPtr; } /* *---------------------------------------------------------------------- * * 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. */ 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: */