diff options
Diffstat (limited to 'unix/tclUnixTime.c')
| -rw-r--r-- | unix/tclUnixTime.c | 584 | 
1 files changed, 357 insertions, 227 deletions
| diff --git a/unix/tclUnixTime.c b/unix/tclUnixTime.c index fc72f35..6a73ac2 100644 --- a/unix/tclUnixTime.c +++ b/unix/tclUnixTime.c @@ -1,54 +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.15 2002/07/19 12:31:10 dkf 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> -#define TM_YEAR_BASE 1900 -#define IsLeapYear(x)   ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0)) +#if defined(TCL_WIDE_CLICKS) && defined(MAC_OSX_TCL) +#include <mach/mach_time.h> +#endif  /* - * 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. + * 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.   */ +#ifndef TCL_NO_DEPRECATED  static Tcl_ThreadDataKey tmKey; +typedef struct { +    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. + * If we fall back on the thread-unsafe versions of gmtime and localtime, use + * this mutex to try to protect them.   */ -#if !defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)  TCL_DECLARE_MUTEX(tmMutex) -#endif + +static char *lastTZ = NULL;	/* Holds the last setting of the TZ +				 * environment variable, or an empty string if +				 * the variable was not set. */  /* - * Forward declarations for procedures defined later in this file. + * Static functions declared in this file.   */ -static struct tm *ThreadSafeGMTime _ANSI_ARGS_(( CONST time_t* )); -static struct tm *ThreadSafeLocalTime _ANSI_ARGS_(( CONST time_t* )); +static void		SetTZIfNecessary(void); +static void		CleanupMemory(ClientData clientData); +#endif /* TCL_NO_DEPRECATED */ + +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. @@ -56,23 +75,23 @@ static struct tm *ThreadSafeLocalTime _ANSI_ARGS_(( CONST time_t* ));   * 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: @@ -81,42 +100,51 @@ TclpGetSeconds()   * Side effects:   *	None.   * - *----------------------------------------------------------------------------- + *----------------------------------------------------------------------   */  unsigned long -TclpGetClicks() +TclpGetClicks(void)  {      unsigned long now; -#ifdef NO_GETTOD -    struct tms dummy; -#else -    struct timeval date; -    struct timezone tz; -#endif  #ifdef NO_GETTOD -    now = (unsigned long) times(&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 -    gettimeofday(&date, &tz); -    now = date.tv_sec*1000000 + date.tv_usec; +    Tcl_Time time; + +    tclGetTimeProcPtr(&time, tclTimeClientData); +    now = time.sec*1000000 + time.usec;  #endif      return now;  } +#ifdef TCL_WIDE_CLICKS  /*   *----------------------------------------------------------------------   * - * TclpGetTimeZone -- + * TclpGetWideClicks --   * - *	Determines the current timezone.  The method varies wildly - *	between different platform implementations, so its hidden in - *	this function. + *	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: - *	The return value is the local time zone, measured in - *	minutes away from GMT (-ve for east, +ve for west). + *	Number of WideInt clicks from some start time.   *   * Side effects:   *	None. @@ -124,127 +152,85 @@ TclpGetClicks()   *----------------------------------------------------------------------   */ -int -TclpGetTimeZone (currentTime) -    unsigned long  currentTime; +Tcl_WideInt +TclpGetWideClicks(void)  { -    /* -     * 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 = ThreadSafeLocalTime(&curTime); -    int         timeZone; - -    timeZone = timeDataPtr->tm_tzadj  / 60; -    if (timeDataPtr->tm_isdst) { -        timeZone += 60; -    } -     -    return timeZone; -#endif +    Tcl_WideInt now; -#if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE) -#   define TCL_GOT_TIMEZONE -    time_t     curTime = (time_t) currentTime; -    struct tm *timeDataPtr = ThreadSafeLocalTime(&curTime); -    int        timeZone; +    if (tclGetTimeProcPtr != NativeGetTime) { +	Tcl_Time time; -    timeZone = -(timeDataPtr->tm_gmtoff / 60); -    if (timeDataPtr->tm_isdst) { -        timeZone += 60; -    } -     -    return timeZone; -#endif - -#if defined(USE_DELTA_FOR_TZ) -#define TCL_GOT_TIMEZONE 1 -    /* -     * 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. -     */ - -    int timeZone; -    time_t tt; -    struct tm *stm; -    tt = 849268800L;      /*    1996-11-29 12:00:00  GMT */ -    stm = ThreadSafeLocalTime(&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 - -    /* -     * 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; +	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 +#error Wide high-resolution clicks not implemented on this platform  #endif -    int        timeZone; - -    Tcl_MutexLock(&tzMutex); -    if (!setTZ) { -        tzset(); -        setTZ = 1;      } -    Tcl_MutexUnlock(&tzMutex); -    /* -     * Note: this is not a typo in "timezone" below!  See tzset -     * documentation for details. -     */ +    return now; +} + +/* + *---------------------------------------------------------------------- + * + * TclpWideClicksToNanoseconds -- + * + *	This procedure converts click values from the TclpGetWideClicks native + *	resolution to nanosecond resolution. + * + * Results: + *	Number of nanoseconds from some start time. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ -    timeZone = timezone / 60; +double +TclpWideClicksToNanoseconds( +    Tcl_WideInt clicks) +{ +    double nsec; -    return timeZone; +    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 - -#if !defined(NO_GETTOD) && !defined (TCL_GOT_TIMEZONE) -#   define TCL_GOT_TIMEZONE -    struct timeval  tv; -    struct timezone tz; -    int timeZone; - -    gettimeofday(&tv, &tz); -    timeZone = tz.tz_minuteswest; -    if (tz.tz_dsttime) { -        timeZone += 60;      } -     -    return timeZone; -#endif - -#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 +    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. + *	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. @@ -256,15 +242,10 @@ TclpGetTimeZone (currentTime)   */  void -Tcl_GetTime(timePtr) -    Tcl_Time *timePtr;		/* Location to store time information. */ +Tcl_GetTime( +    Tcl_Time *timePtr)		/* Location to store time information. */  { -    struct timeval tv; -    struct timezone tz; -     -    (void) gettimeofday(&tv, &tz); -    timePtr->sec = tv.tv_sec; -    timePtr->usec = tv.tv_usec; +    tclGetTimeProcPtr(timePtr, tclTimeClientData);  }  /* @@ -272,9 +253,9 @@ Tcl_GetTime(timePtr)   *   * 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. @@ -285,134 +266,283 @@ Tcl_GetTime(timePtr)   *----------------------------------------------------------------------   */ +#ifndef TCL_NO_DEPRECATED  struct tm * -TclpGetDate(time, useGMT) -    TclpTime_t time; -    int useGMT; +TclpGetDate( +    const time_t *time, +    int useGMT)  { -    CONST time_t *tp = (CONST time_t *)time; -      if (useGMT) { -	return ThreadSafeGMTime(tp); +	return TclpGmtime(time);      } else { -	return ThreadSafeLocalTime(tp); +	return TclpLocaltime(time);      }  }  /*   *----------------------------------------------------------------------   * - * TclpStrftime -- + * TclpGmtime --   * - *	On Unix, we can safely call the native strftime implementation, - *	and also ignore the useGMT parameter. + *	Wrapper around the 'gmtime' library function to make it thread safe.   *   * Results: - *	The normal strftime result. + *	Returns a pointer to a 'struct tm' in thread-specific data.   *   * Side effects: - *	None. + *	Invokes gmtime or gmtime_r as appropriate.   *   *----------------------------------------------------------------------   */ -size_t -TclpStrftime(s, maxsize, format, t, useGMT) -    char *s; -    size_t maxsize; -    CONST char *format; -    CONST struct tm *t; -    int useGMT; +struct tm * +TclpGmtime( +    const time_t *timePtr)	/* Pointer to the number of seconds since the +				 * local system's epoch */  { -    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)); -    } -    setlocale(LC_TIME, ""); -    return strftime(s, maxsize, format, t); +    /* +     * Get a thread-local buffer to hold the returned time. +     */ + +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey); + +#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 + +    return &tsdPtr->gmtime_buf;  }  /*   *----------------------------------------------------------------------   * - * ThreadSafeGMTime -- + * TclpLocaltime --   * - *	Wrapper around the 'gmtime' library function to make it thread + *	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 gmtime or gmtime_r as appropriate. + *	Invokes localtime or localtime_r as appropriate.   *   *----------------------------------------------------------------------   */ -static struct tm * -ThreadSafeGMTime(timePtr) -    CONST time_t *timePtr;	/* Pointer to the number of seconds -				 * since the local system's epoch -				 */ - +struct tm * +TclpLocaltime( +    const time_t *timePtr)	/* Pointer to the number of seconds since the +				 * local system's epoch */  {      /*       * Get a thread-local buffer to hold the returned time.       */ -    struct tm *tmPtr = (struct tm *) -	    Tcl_GetThreadData(&tmKey, sizeof(struct tm)); -#ifdef HAVE_GMTIME_R -    gmtime_r(timePtr, tmPtr); +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey); + +    SetTZIfNecessary(); +#ifdef HAVE_LOCALTIME_R +    localtime_r(timePtr, &tsdPtr->localtime_buf);  #else      Tcl_MutexLock(&tmMutex); -    memcpy((VOID *) tmPtr, (VOID *) gmtime(timePtr), sizeof(struct tm)); +    memcpy(&tsdPtr->localtime_buf, localtime(timePtr), sizeof(struct tm));      Tcl_MutexUnlock(&tmMutex); -#endif     -    return tmPtr; +#endif + +    return &tsdPtr->localtime_buf;  } +#endif /* TCL_NO_DEPRECATED */  /*   *----------------------------------------------------------------------   * - * ThreadSafeLocalTime -- + * Tcl_SetTimeProc --   * - *	Wrapper around the 'localtime' library function to make it thread - *	safe. + *	TIP #233 (Virtualized Time): Registers two handlers for the + *	virtualization of Tcl's access to time information.   *   * Results: - *	Returns a pointer to a 'struct tm' in thread-specific data. + *	None.   *   * Side effects: - *	Invokes localtime or localtime_r as appropriate. + *	Remembers the handlers, alters core behaviour.   *   *----------------------------------------------------------------------   */ -static struct tm * -ThreadSafeLocalTime(timePtr) -    CONST time_t *timePtr;	/* Pointer to the number of seconds -				 * since the local system's epoch -				 */ +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)  { -    /* -     * Get a thread-local buffer to hold the returned time. -     */ +    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 */ +} + +/* + *---------------------------------------------------------------------- + * + * 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: + *	None. + * + *---------------------------------------------------------------------- + */ + +static void +NativeGetTime( +    Tcl_Time *timePtr, +    ClientData clientData) +{ +    struct timeval tv; + +    (void) gettimeofday(&tv, NULL); +    timePtr->sec = tv.tv_sec; +    timePtr->usec = tv.tv_usec; +} +/* + *---------------------------------------------------------------------- + * + * SetTZIfNecessary -- + * + *	Determines whether a call to 'tzset' is needed prior to the next call + *	to 'localtime' or examination of the 'timezone' variable. + * + * Results: + *	None. + * + * Side effects: + *	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. + * + *---------------------------------------------------------------------- + */ + +#ifndef TCL_NO_DEPRECATED +static void +SetTZIfNecessary(void) +{ +    const char *newTZ = getenv("TZ"); -    struct tm *tmPtr = (struct tm *) -	    Tcl_GetThreadData(&tmKey, sizeof(struct tm)); -#ifdef HAVE_LOCALTIME_R -    localtime_r(timePtr, tmPtr); -#else      Tcl_MutexLock(&tmMutex); -    memcpy((VOID *) tmPtr, (VOID *) localtime(timePtr), sizeof(struct tm)); +    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); -#endif     -    return tmPtr;  } + +/* + *---------------------------------------------------------------------- + * + * CleanupMemory -- + * + *	Releases the private copy of the TZ environment variable upon exit + *	from Tcl. + * + * Results: + *	None. + * + * Side effects: + *	Frees allocated memory. + * + *---------------------------------------------------------------------- + */ + +static void +CleanupMemory( +    ClientData ignored) +{ +    ckfree(lastTZ); +} +#endif /* TCL_NO_DEPRECATED */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
