diff options
Diffstat (limited to 'unix/tclUnixTime.c')
-rw-r--r-- | unix/tclUnixTime.c | 568 |
1 files changed, 401 insertions, 167 deletions
diff --git a/unix/tclUnixTime.c b/unix/tclUnixTime.c index 9990aad..926e8f4 100644 --- a/unix/tclUnixTime.c +++ b/unix/tclUnixTime.c @@ -1,29 +1,73 @@ -/* +/* * tclUnixTime.c -- * - * Contains Unix specific versions of Tcl functions that - * obtain time values from the operating system. + * Contains Unix specific versions of Tcl functions that obtain time + * values from the operating system. * * Copyright (c) 1995 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: tclUnixTime.c,v 1.7 2000/01/14 22:15:52 ericm Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" -#include "tclPort.h" +#include <locale.h> +#if defined(TCL_WIDE_CLICKS) && defined(MAC_OSX_TCL) +#include <mach/mach_time.h> +#endif + #define TM_YEAR_BASE 1900 -#define IsLeapYear(x) ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0)) +#define IsLeapYear(x) (((x)%4 == 0) && ((x)%100 != 0 || (x)%400 == 0)) + +/* + * TclpGetDate is coded to return a pointer to a 'struct tm'. For thread + * safety, this structure must be in thread-specific data. The 'tmKey' + * variable is the key to this buffer. + */ + +static Tcl_ThreadDataKey tmKey; +typedef struct ThreadSpecificData { + struct tm gmtime_buf; + struct tm localtime_buf; +} ThreadSpecificData; + +/* + * If we fall back on the thread-unsafe versions of gmtime and localtime, use + * this mutex to try to protect them. + */ + +TCL_DECLARE_MUTEX(tmMutex) + +static char *lastTZ = NULL; /* Holds the last setting of the TZ + * environment variable, or an empty string if + * the variable was not set. */ + +/* + * Static functions declared in this file. + */ + +static void SetTZIfNecessary(void); +static void CleanupMemory(ClientData clientData); +static void NativeScaleTime(Tcl_Time *timebuf, + ClientData clientData); +static void NativeGetTime(Tcl_Time *timebuf, + ClientData clientData); + +/* + * TIP #233 (Virtualized Time): Data for the time hooks, if any. + */ + +Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime; +Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime; +ClientData tclTimeClientData = NULL; /* - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- * * TclpGetSeconds -- * - * This procedure returns the number of seconds from the epoch. On - * most Unix systems the epoch is Midnight Jan 1, 1970 GMT. + * This procedure returns the number of seconds from the epoch. On most + * Unix systems the epoch is Midnight Jan 1, 1970 GMT. * * Results: * Number of seconds from the epoch. @@ -31,23 +75,23 @@ * Side effects: * None. * - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- */ unsigned long -TclpGetSeconds() +TclpGetSeconds(void) { - return time((time_t *) NULL); + return time(NULL); } /* - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- * * TclpGetClicks -- * * This procedure returns a value that represents the highest resolution - * clock available on the system. There are no garantees on what the - * resolution will be. In Tcl we will call this value a "click". The + * clock available on the system. There are no garantees on what the + * resolution will be. In Tcl we will call this value a "click". The * start time is also system dependant. * * Results: @@ -56,26 +100,75 @@ TclpGetSeconds() * Side effects: * None. * - *----------------------------------------------------------------------------- + *---------------------------------------------------------------------- */ unsigned long -TclpGetClicks() +TclpGetClicks(void) { unsigned long now; + #ifdef NO_GETTOD - struct tms dummy; + if (tclGetTimeProcPtr != NativeGetTime) { + Tcl_Time time; + + tclGetTimeProcPtr(&time, tclTimeClientData); + now = time.sec*1000000 + time.usec; + } else { + /* + * A semi-NativeGetTime, specialized to clicks. + */ + struct tms dummy; + + now = (unsigned long) times(&dummy); + } #else - struct timeval date; - struct timezone tz; + Tcl_Time time; + + tclGetTimeProcPtr(&time, tclTimeClientData); + now = time.sec*1000000 + time.usec; #endif -#ifdef NO_GETTOD - now = (unsigned long) times(&dummy); + return now; +} +#ifdef TCL_WIDE_CLICKS + +/* + *---------------------------------------------------------------------- + * + * TclpGetWideClicks -- + * + * This procedure returns a WideInt value that represents the highest + * resolution clock available on the system. There are no garantees 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 WideInt clicks from some start time. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_WideInt +TclpGetWideClicks(void) +{ + Tcl_WideInt now; + + if (tclGetTimeProcPtr != NativeGetTime) { + Tcl_Time time; + + tclGetTimeProcPtr(&time, tclTimeClientData); + now = (Tcl_WideInt) (time.sec*1000000 + time.usec); + } else { +#ifdef MAC_OSX_TCL + now = (Tcl_WideInt) (mach_absolute_time() & INT64_MAX); #else - gettimeofday(&date, &tz); - now = date.tv_sec*1000000 + date.tv_usec; +#error Wide high-resolution clicks not implemented on this platform #endif + } return now; } @@ -83,15 +176,13 @@ TclpGetClicks() /* *---------------------------------------------------------------------- * - * TclpGetTimeZone -- + * TclpWideClicksToNanoseconds -- * - * Determines the current timezone. The method varies wildly - * between different platform implementations, so its hidden in - * this function. + * This procedure converts click values from the TclpGetWideClicks native + * resolution to nanosecond resolution. * * Results: - * The return value is the local time zone, measured in - * minutes away from GMT (-ve for east, +ve for west). + * Number of nanoseconds from some start time. * * Side effects: * None. @@ -99,126 +190,264 @@ TclpGetClicks() *---------------------------------------------------------------------- */ -int -TclpGetTimeZone (currentTime) - unsigned long currentTime; +double +TclpWideClicksToNanoseconds( + Tcl_WideInt clicks) { - /* - * Determine how a timezone is obtained from "struct tm". If there is no - * time zone in this struct (very lame) then use the timezone variable. - * This is done in a way to make the timezone variable the method of last - * resort, as some systems have it in addition to a field in "struct tm". - * The gettimeofday system call can also be used to determine the time - * zone. - */ - -#if defined(HAVE_TM_TZADJ) -# define TCL_GOT_TIMEZONE - time_t curTime = (time_t) currentTime; - struct tm *timeDataPtr = localtime(&curTime); - int timeZone; - - timeZone = timeDataPtr->tm_tzadj / 60; - if (timeDataPtr->tm_isdst) { - timeZone += 60; - } - - return timeZone; + double nsec; + + if (tclGetTimeProcPtr != NativeGetTime) { + nsec = clicks * 1000; + } else { +#ifdef MAC_OSX_TCL + static mach_timebase_info_data_t tb; + static uint64_t maxClicksForUInt64; + + if (!tb.denom) { + mach_timebase_info(&tb); + maxClicksForUInt64 = UINT64_MAX / tb.numer; + } + if ((uint64_t) clicks < maxClicksForUInt64) { + nsec = ((uint64_t) clicks) * tb.numer / tb.denom; + } else { + nsec = ((long double) (uint64_t) clicks) * tb.numer / tb.denom; + } +#else +#error Wide high-resolution clicks not implemented on this platform #endif + } + + return nsec; +} +#endif /* TCL_WIDE_CLICKS */ + +/* + *---------------------------------------------------------------------- + * + * Tcl_GetTime -- + * + * Gets the current system time in seconds and microseconds since the + * beginning of the epoch: 00:00 UCT, January 1, 1970. + * + * This function is hooked, allowing users to specify their own virtual + * system time. + * + * Results: + * Returns the current time in timePtr. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ -#if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE) -# define TCL_GOT_TIMEZONE - time_t curTime = (time_t) currentTime; - struct tm *timeDataPtr = localtime(&curTime); - int timeZone; +void +Tcl_GetTime( + Tcl_Time *timePtr) /* Location to store time information. */ +{ + tclGetTimeProcPtr(timePtr, tclTimeClientData); +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + *---------------------------------------------------------------------- + */ - timeZone = -(timeDataPtr->tm_gmtoff / 60); - if (timeDataPtr->tm_isdst) { - timeZone += 60; +struct tm * +TclpGetDate( + const time_t *time, + int useGMT) +{ + if (useGMT) { + return TclpGmtime(time); + } else { + return TclpLocaltime(time); } - - return timeZone; -#endif +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + *---------------------------------------------------------------------- + */ -#if defined(USE_DELTA_FOR_TZ) -#define TCL_GOT_TIMEZONE 1 +struct tm * +TclpGmtime( + const time_t *timePtr) /* Pointer to the number of seconds since the + * local system's epoch */ +{ /* - * This hack replaces using global var timezone or gettimeofday - * in situations where they are buggy such as on AIX when libbsd.a - * is linked in. + * Get a thread-local buffer to hold the returned time. */ - int timeZone; - time_t tt; - struct tm *stm; - tt = 849268800L; /* 1996-11-29 12:00:00 GMT */ - stm = localtime(&tt); /* eg 1996-11-29 6:00:00 CST6CDT */ - /* The calculation below assumes a max of +12 or -12 hours from GMT */ - timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min); - return timeZone; /* eg +360 for CST6CDT */ -#endif + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey); - /* - * Must prefer timezone variable over gettimeofday, as gettimeofday does - * not return timezone information on many systems that have moved this - * information outside of the kernel. - */ - -#if defined(HAVE_TIMEZONE_VAR) && !defined (TCL_GOT_TIMEZONE) -# define TCL_GOT_TIMEZONE - static int setTZ = 0; -#ifdef TCL_THREADS - static Tcl_Mutex tzMutex; +#ifdef HAVE_GMTIME_R + gmtime_r(timePtr, &tsdPtr->gmtime_buf); +#else + Tcl_MutexLock(&tmMutex); + memcpy(&tsdPtr->gmtime_buf, gmtime(timePtr), sizeof(struct tm)); + Tcl_MutexUnlock(&tmMutex); #endif - int timeZone; - Tcl_MutexLock(&tzMutex); - if (!setTZ) { - tzset(); - setTZ = 1; - } - Tcl_MutexUnlock(&tzMutex); + return &tsdPtr->gmtime_buf; +} + +/* + *---------------------------------------------------------------------- + * + * 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 */ +{ /* - * Note: this is not a typo in "timezone" below! See tzset - * documentation for details. + * Get a thread-local buffer to hold the returned time. */ - timeZone = timezone / 60; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey); - return timeZone; + SetTZIfNecessary(); +#ifdef HAVE_LOCALTIME_R + localtime_r(timePtr, &tsdPtr->localtime_buf); +#else + Tcl_MutexLock(&tmMutex); + memcpy(&tsdPtr->localtime_buf, localtime(timePtr), sizeof(struct tm)); + Tcl_MutexUnlock(&tmMutex); #endif -#if !defined(NO_GETTOD) && !defined (TCL_GOT_TIMEZONE) -# define TCL_GOT_TIMEZONE - struct timeval tv; - struct timezone tz; - int timeZone; + return &tsdPtr->localtime_buf; +} + +/* + *---------------------------------------------------------------------- + * + * 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. + * + *---------------------------------------------------------------------- + */ - gettimeofday(&tv, &tz); - timeZone = tz.tz_minuteswest; - if (tz.tz_dsttime) { - timeZone += 60; - } - - return timeZone; -#endif +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. + * + *---------------------------------------------------------------------- + */ -#ifndef TCL_GOT_TIMEZONE - /* - * Cause compile error, we don't know how to get timezone. - */ - error: autoconf did not figure out how to determine the timezone. -#endif +void +Tcl_QueryTimeProc( + Tcl_GetTimeProc **getProc, + Tcl_ScaleTimeProc **scaleProc, + ClientData *clientData) +{ + if (getProc) { + *getProc = tclGetTimeProcPtr; + } + if (scaleProc) { + *scaleProc = tclScaleTimeProcPtr; + } + if (clientData) { + *clientData = 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 */ } /* *---------------------------------------------------------------------- * - * 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: @@ -230,81 +459,86 @@ TclpGetTimeZone (currentTime) *---------------------------------------------------------------------- */ -void -TclpGetTime(timePtr) - Tcl_Time *timePtr; /* Location to store time information. */ +static void +NativeGetTime( + Tcl_Time *timePtr, + ClientData clientData) { struct timeval tv; - struct timezone tz; - - (void) gettimeofday(&tv, &tz); + + (void) gettimeofday(&tv, NULL); timePtr->sec = tv.tv_sec; timePtr->usec = tv.tv_usec; } - /* *---------------------------------------------------------------------- * - * TclpGetDate -- + * SetTZIfNecessary -- * - * 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. + * Determines whether a call to 'tzset' is needed prior to the next call + * to 'localtime' or examination of the 'timezone' variable. * * Results: - * Returns a static tm structure. + * None. * * Side effects: - * None. + * If 'tzset' has never been called in the current process, or if the + * value of the environment variable TZ has changed since the last call + * to 'tzset', then 'tzset' is called again. * *---------------------------------------------------------------------- */ -struct tm * -TclpGetDate(time, useGMT) - TclpTime_t time; - int useGMT; +static void +SetTZIfNecessary(void) { - CONST time_t *tp = (CONST time_t *)time; + const char *newTZ = getenv("TZ"); - if (useGMT) { - return gmtime(tp); - } else { - return localtime(tp); + Tcl_MutexLock(&tmMutex); + if (newTZ == NULL) { + newTZ = ""; + } + if (lastTZ == NULL || strcmp(lastTZ, newTZ)) { + tzset(); + if (lastTZ == NULL) { + Tcl_CreateExitHandler(CleanupMemory, NULL); + } else { + ckfree(lastTZ); + } + lastTZ = ckalloc(strlen(newTZ) + 1); + strcpy(lastTZ, newTZ); } + Tcl_MutexUnlock(&tmMutex); } /* *---------------------------------------------------------------------- * - * TclpStrftime -- + * CleanupMemory -- * - * On Unix, we can safely call the native strftime implementation. + * Releases the private copy of the TZ environment variable upon exit + * from Tcl. * * Results: - * The normal strftime result. + * None. * * Side effects: - * None. + * Frees allocated memory. * *---------------------------------------------------------------------- */ -size_t -TclpStrftime(s, maxsize, format, t) - char *s; - size_t maxsize; - CONST char *format; - CONST struct tm *t; +static void +CleanupMemory( + ClientData ignored) { - if (format[0] == '%' && format[1] == 'Q') { - /* Format as a stardate */ - sprintf(s, "Stardate %2d%03d.%01d", - (((t->tm_year + TM_YEAR_BASE) + 377) - 2323), - (((t->tm_yday + 1) * 1000) / - (365 + IsLeapYear((t->tm_year + TM_YEAR_BASE)))), - (((t->tm_hour * 60) + t->tm_min)/144)); - return(strlen(s)); - } - return strftime(s, maxsize, format, t); + ckfree(lastTZ); } + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |