📄 tclwintime.c
字号:
/* * 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. * * RCS: @(#) $Id: tclWinTime.c,v 1.11 2002/10/09 23:57:25 kennykb Exp $ */#include "tclWinInt.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 int normalDays[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364};static 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;/* * Calibration interval for the high-resolution timer, in msec */static CONST unsigned long clockCalibrateWakeupInterval = 10000; /* FIXME: 10 s -- should be about 10 min! *//* * Data for managing high-resolution timers. */typedef struct TimeInfo { CRITICAL_SECTION cs; /* Mutex guarding this structure */ int initialized; /* Flag == 1 if this structure is * initialized. */ int perfCounterAvailable; /* Flag == 1 if the hardware has a * performance counter */ HANDLE calibrationThread; /* Handle to the thread that keeps the * virtual clock calibrated. */ HANDLE readyEvent; /* System event used to * trigger the requesting thread * when the clock calibration procedure * is initialized for the first time */ HANDLE exitEvent; /* Event to signal out of an exit handler * to tell the calibration loop to * terminate */ /* * The following values are used for calculating virtual time. * Virtual time is always equal to: * lastFileTime + (current perf counter - lastCounter) * * 10000000 / curCounterFreq * and lastFileTime and lastCounter are updated any time that * virtual time is returned to a caller. */ ULARGE_INTEGER lastFileTime; LARGE_INTEGER lastCounter; LARGE_INTEGER curCounterFreq; /* * The next two values are used only in the calibration thread, to track * the frequency of the performance counter. */ LONGLONG lastPerfCounter; /* Performance counter the last time * that UpdateClockEachSecond was called */ LONGLONG lastSysTime; /* System clock at the last time * that UpdateClockEachSecond was called */ LONGLONG estPerfCounterFreq; /* Current estimate of the counter frequency * using the system clock as the standard */} TimeInfo;static TimeInfo timeInfo = { { NULL }, 0, 0, (HANDLE) NULL, (HANDLE) NULL, (HANDLE) NULL, 0, 0, 0, 0, 0, 0};CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE }; /* * Declarations for functions defined later in this file. */static struct tm * ComputeGMT _ANSI_ARGS_((const time_t *tp));static void StopCalibration _ANSI_ARGS_(( ClientData ));static DWORD WINAPI CalibrationThread _ANSI_ARGS_(( LPVOID arg ));static void UpdateTimeEachSecond _ANSI_ARGS_(( void ));/* *---------------------------------------------------------------------- * * 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 longTclpGetSeconds(){ Tcl_Time t; Tcl_GetTime( &t ); 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 longTclpGetClicks(){ /* * Use the Tcl_GetTime abstraction to get the time in microseconds, * as nearly as we can, and return it. */ Tcl_Time now; /* Current Tcl time */ unsigned long retval; /* Value to return */ Tcl_GetTime( &now ); retval = ( now.sec * 1000000 ) + now.usec; return retval;}/* *---------------------------------------------------------------------- * * TclpGetTimeZone -- * * Determines the current timezone. The method varies wildly * between different Platform implementations, so its hidden in * this function. * * Results: * Minutes west of GMT. * * Side effects: * None. * *---------------------------------------------------------------------- */intTclpGetTimeZone (currentTime) unsigned long currentTime;{ int timeZone; tzset(); timeZone = _timezone / 60; return timeZone;}/* *---------------------------------------------------------------------- * * 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. * *---------------------------------------------------------------------- */voidTcl_GetTime(timePtr) Tcl_Time *timePtr; /* Location to store time information. */{ struct timeb t; /* Initialize static storage on the first trip through. */ /* * Note: Outer check for 'initialized' is a performance win * since it avoids an extra mutex lock in the common case. */ if ( !timeInfo.initialized ) { TclpInitLock(); if ( !timeInfo.initialized ) { timeInfo.perfCounterAvailable = QueryPerformanceFrequency( &timeInfo.curCounterFreq ); /* * Some hardware abstraction layers use the CPU clock * in place of the real-time clock as a performance counter * reference. This results in: * - inconsistent results among the processors on * multi-processor systems. * - unpredictable changes in performance counter frequency * on "gearshift" processors such as Transmeta and * SpeedStep. * * There seems to be no way to test whether the performance * counter is reliable, but a useful heuristic is that * if its frequency is 1.193182 MHz or 3.579545 MHz, it's * derived from a colorburst crystal and is therefore * the RTC rather than the TSC. * * 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. */ if ( timeInfo.perfCounterAvailable /* The following lines would do an exact match on * crystal frequency: * && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 1193182 * && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 3579545 */ && timeInfo.curCounterFreq.QuadPart > (LONGLONG) 15000000 ) { timeInfo.perfCounterAvailable = FALSE; } /* * If the performance counter is available, start a thread to * calibrate it. */ if ( timeInfo.perfCounterAvailable ) { DWORD id; InitializeCriticalSection( &timeInfo.cs ); timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); timeInfo.calibrationThread = CreateThread( NULL, 8192, 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, (ClientData) NULL ); } timeInfo.initialized = TRUE; } TclpInitUnlock(); } if ( timeInfo.perfCounterAvailable ) { /* * Query the performance counter and use it to calculate the * current time. */ LARGE_INTEGER curCounter; /* Current performance counter */ LONGLONG curFileTime; /* Current estimated time, expressed * as 100-ns ticks since the Windows epoch */ static LARGE_INTEGER posixEpoch; /* Posix epoch expressed as 100-ns ticks * since the windows epoch */ LONGLONG 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; timePtr->usec = t.millitm * 1000; }}/* *---------------------------------------------------------------------- * * StopCalibration -- * * Turns off the calibration thread in preparation for exiting the * process. * * Results: * None. * * Side effects: * Sets the 'exitEvent' event in the 'timeInfo' structure to ask * the thread in question to exit, and waits for it to do so. * *---------------------------------------------------------------------- */static voidStopCalibration( ClientData unused ) /* Client data is unused */{ SetEvent( timeInfo.exitEvent ); WaitForSingleObject( timeInfo.calibrationThread, INFINITE ); CloseHandle( timeInfo.exitEvent ); CloseHandle( timeInfo.calibrationThread );}/* *---------------------------------------------------------------------- * * 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 * zone 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) { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -