From c093e76184fc3101487abf86878fcc8e85a77cd9 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 10 Jan 2017 22:04:49 +0000 Subject: [temp-commit]: ClockFreeScan back-ported (cherry picked), all tests case passed + several new test-cases for bug fixing implemented here; environment epoch ported, several fixes for the time zone / tzdata caching ported; mem-leak fix + memory leak test cases passed --- generic/tclClock.c | 342 +++++++++++++++++++++++++++------------------- generic/tclDate.h | 13 +- generic/tclEnv.c | 8 ++ library/clock.tcl | 12 +- tests-perf/clock.perf.tcl | 78 +++++++---- tests/clock.test | 21 +++ 6 files changed, 296 insertions(+), 178 deletions(-) diff --git a/generic/tclClock.c b/generic/tclClock.c index 7256320..e39e032 100644 --- a/generic/tclClock.c +++ b/generic/tclClock.c @@ -115,14 +115,24 @@ typedef struct ClockClientData { size_t refCount; /* Number of live references. */ Tcl_Obj **literals; /* Pool of object literals. */ /* Cache for current clock parameters, imparted via "configure" */ - unsigned int LastTZEpoch; - Tcl_Obj *LastSystemTimeZone; + unsigned long LastTZEpoch; + Tcl_Obj *SystemTimeZone; Tcl_Obj *SystemSetupTZData; Tcl_Obj *GMTSetupTimeZone; Tcl_Obj *GMTSetupTZData; + Tcl_Obj *AnySetupTimeZone; + Tcl_Obj *AnySetupTZData; + Tcl_Obj *LastUnnormSetupTimeZone; Tcl_Obj *LastSetupTimeZone; Tcl_Obj *LastSetupTZData; - Tcl_Obj *LastUsedSetupTimeZone; + /* + /* [SB] TODO: back-port (from tclSE) the same date caching ... + * Cache for last date (fast convert if date parsed was the same) * / + struct { + DateInfo yy; + TclDateFields date; + } lastDate; + */ } ClockClientData; /* @@ -233,11 +243,29 @@ static int ClockFreeScan( Tcl_Obj *strObj, Tcl_WideInt baseVal, Tcl_Obj *timezoneObj, Tcl_Obj *locale); static struct tm * ThreadSafeLocalTime(const time_t *); -static unsigned int TzsetGetEpoch(void); +static unsigned long TzsetGetEpoch(void); static void TzsetIfNecessary(void); static void ClockDeleteCmdProc(ClientData); /* + * Primitives to safe set, reset and free references. + */ + +#define Tcl_UnsetObjRef(obj) \ + if (obj != NULL) { Tcl_DecrRefCount(obj); obj = NULL; } +#define Tcl_InitObjRef(obj, val) \ + obj = val; if (obj) { Tcl_IncrRefCount(obj); } +#define Tcl_SetObjRef(obj, val) \ +if (1) { \ + Tcl_Obj *nval = val; \ + if (obj != nval) { \ + Tcl_Obj *prev = obj; \ + Tcl_InitObjRef(obj, nval); \ + if (prev != NULL) { Tcl_DecrRefCount(prev); }; \ + } \ +} + +/* * Structure containing description of "native" clock commands to create. */ @@ -319,13 +347,15 @@ TclClockInit( Tcl_IncrRefCount(data->literals[i]); } data->LastTZEpoch = 0; - data->LastSystemTimeZone = NULL; + data->SystemTimeZone = NULL; data->SystemSetupTZData = NULL; data->GMTSetupTimeZone = NULL; data->GMTSetupTZData = NULL; + data->AnySetupTimeZone = NULL; + data->AnySetupTZData = NULL; + data->LastUnnormSetupTimeZone = NULL; data->LastSetupTimeZone = NULL; data->LastSetupTZData = NULL; - data->LastUsedSetupTimeZone = NULL; /* * Install the commands. @@ -356,6 +386,22 @@ TclClockInit( */ static void +ClockConfigureClear( + ClockClientData *data) +{ + data->LastTZEpoch = 0; + Tcl_UnsetObjRef(data->SystemTimeZone); + Tcl_UnsetObjRef(data->SystemSetupTZData); + Tcl_UnsetObjRef(data->GMTSetupTimeZone); + Tcl_UnsetObjRef(data->GMTSetupTZData); + Tcl_UnsetObjRef(data->AnySetupTimeZone); + Tcl_UnsetObjRef(data->AnySetupTZData); + Tcl_UnsetObjRef(data->LastUnnormSetupTimeZone); + Tcl_UnsetObjRef(data->LastSetupTimeZone); + Tcl_UnsetObjRef(data->LastSetupTZData); +} + +static void ClockDeleteCmdProc( ClientData clientData) /* Opaque pointer to the client data */ { @@ -367,15 +413,7 @@ ClockDeleteCmdProc( Tcl_DecrRefCount(data->literals[i]); } - if (data->LastSystemTimeZone) { - Tcl_DecrRefCount(data->LastSystemTimeZone); - } - if (data->GMTSetupTimeZone) { - Tcl_DecrRefCount(data->GMTSetupTimeZone); - } - if (data->LastSetupTimeZone) { - Tcl_DecrRefCount(data->LastSetupTimeZone); - } + ClockConfigureClear(data); ckfree(data->literals); ckfree(data); @@ -391,34 +429,40 @@ NormTimezoneObj( Tcl_Obj * timezoneObj) { const char * tz; - if ( timezoneObj == dataPtr->literals[LIT_GMT] - || timezoneObj == dataPtr->LastSystemTimeZone - || timezoneObj == dataPtr->LastSetupTimeZone + if ( timezoneObj == dataPtr->LastUnnormSetupTimeZone + && dataPtr->LastSetupTimeZone != NULL + ) { + return dataPtr->LastSetupTimeZone; + } + if ( timezoneObj == dataPtr->LastSetupTimeZone + || timezoneObj == dataPtr->literals[LIT_GMT] + || timezoneObj == dataPtr->SystemTimeZone + || timezoneObj == dataPtr->AnySetupTimeZone ) { return timezoneObj; } tz = TclGetString(timezoneObj); - if ( - strcmp(tz, Literals[LIT_GMT]) == 0 + if (dataPtr->AnySetupTimeZone != NULL && + (timezoneObj == dataPtr->AnySetupTimeZone + || strcmp(tz, TclGetString(dataPtr->AnySetupTimeZone)) == 0 + ) ) { - timezoneObj = dataPtr->literals[LIT_GMT]; + timezoneObj = dataPtr->AnySetupTimeZone; } else - if (dataPtr->LastSystemTimeZone != NULL && - (timezoneObj == dataPtr->LastSystemTimeZone - || strcmp(tz, TclGetString(dataPtr->LastSystemTimeZone)) == 0 + if (dataPtr->SystemTimeZone != NULL && + (timezoneObj == dataPtr->SystemTimeZone + || strcmp(tz, TclGetString(dataPtr->SystemTimeZone)) == 0 ) ) { - timezoneObj = dataPtr->LastSystemTimeZone; + timezoneObj = dataPtr->SystemTimeZone; } else - if (dataPtr->LastSetupTimeZone != NULL && - (timezoneObj == dataPtr->LastSetupTimeZone - || strcmp(tz, TclGetString(dataPtr->LastSetupTimeZone)) == 0 - ) + if ( + strcmp(tz, Literals[LIT_GMT]) == 0 ) { - timezoneObj = dataPtr->LastSetupTimeZone; + timezoneObj = dataPtr->literals[LIT_GMT]; } return timezoneObj; } @@ -454,74 +498,64 @@ ClockConfigureObjCmd( Tcl_GetString(objv[i]), NULL); return TCL_ERROR; } - if (optionIndex == CLOCK_SYSTEM_TZ || optionIndex == CLOCK_CLEAR_CACHE) { - if (dataPtr->LastSystemTimeZone) { - Tcl_DecrRefCount(dataPtr->LastSystemTimeZone); - dataPtr->LastSystemTimeZone = NULL; - dataPtr->SystemSetupTZData = NULL; - } - if (optionIndex != CLOCK_CLEAR_CACHE) { - /* validate current tz-epoch */ - unsigned int lastTZEpoch = TzsetGetEpoch(); - if (i+1 < objc) { - Tcl_IncrRefCount( - dataPtr->LastSystemTimeZone = objv[i+1]); - dataPtr->LastTZEpoch = lastTZEpoch; - } else if (dataPtr->LastSystemTimeZone - && dataPtr->LastTZEpoch == lastTZEpoch) { - Tcl_SetObjResult(interp, dataPtr->LastSystemTimeZone); + switch (optionIndex) { + case CLOCK_SYSTEM_TZ: + if (1) { + /* validate current tz-epoch */ + unsigned long lastTZEpoch = TzsetGetEpoch(); + if (i+1 < objc) { + if (dataPtr->SystemTimeZone != objv[i+1]) { + Tcl_SetObjRef(dataPtr->SystemTimeZone, objv[i+1]); + Tcl_UnsetObjRef(dataPtr->SystemSetupTZData); } + dataPtr->LastTZEpoch = lastTZEpoch; + } + if (dataPtr->SystemTimeZone != NULL + && dataPtr->LastTZEpoch == lastTZEpoch) { + Tcl_SetObjResult(interp, dataPtr->SystemTimeZone); } } - if (optionIndex == CLOCK_SETUP_TZ || optionIndex == CLOCK_CLEAR_CACHE) { - Tcl_Obj *timezoneObj = NULL; - /* differentiate GMT and system zones, because used often */ + break; + case CLOCK_SETUP_TZ: if (i+1 < objc) { - timezoneObj = NormTimezoneObj(dataPtr, objv[i+1]); - if (optionIndex == CLOCK_SETUP_TZ) { - dataPtr->LastUsedSetupTimeZone = timezoneObj; - if (timezoneObj == litPtr[LIT_GMT]) { - optionIndex = CLOCK_SETUP_GMT; - } else if (timezoneObj == dataPtr->LastSystemTimeZone) { - optionIndex = CLOCK_SETUP_NOP; - } + /* differentiate GMT and system zones, because used often */ + Tcl_Obj *timezoneObj = NormTimezoneObj(dataPtr, objv[i+1]); + Tcl_SetObjRef(dataPtr->LastUnnormSetupTimeZone, objv[i+1]); + if (dataPtr->LastSetupTimeZone != timezoneObj) { + Tcl_SetObjRef(dataPtr->LastSetupTimeZone, timezoneObj); + Tcl_UnsetObjRef(dataPtr->LastSetupTZData); } - } - - if (optionIndex == CLOCK_SETUP_GMT || optionIndex == CLOCK_CLEAR_CACHE) { - if (dataPtr->GMTSetupTimeZone) { - Tcl_DecrRefCount(dataPtr->GMTSetupTimeZone); - dataPtr->GMTSetupTimeZone = NULL; - dataPtr->GMTSetupTZData = NULL; + if (timezoneObj == litPtr[LIT_GMT]) { + optionIndex = CLOCK_SETUP_GMT; + } else if (timezoneObj == dataPtr->SystemTimeZone) { + optionIndex = CLOCK_SETUP_NOP; } - if (optionIndex != CLOCK_CLEAR_CACHE) { + switch (optionIndex) { + case CLOCK_SETUP_GMT: if (i+1 < objc) { - Tcl_IncrRefCount( - dataPtr->GMTSetupTimeZone = timezoneObj); - } else if (dataPtr->GMTSetupTimeZone) { - Tcl_SetObjResult(interp, dataPtr->GMTSetupTimeZone); + if (dataPtr->GMTSetupTimeZone != timezoneObj) { + Tcl_SetObjRef(dataPtr->GMTSetupTimeZone, timezoneObj); + Tcl_UnsetObjRef(dataPtr->GMTSetupTZData); + } } - } - } - if (optionIndex == CLOCK_SETUP_TZ || optionIndex == CLOCK_CLEAR_CACHE) { - if (dataPtr->LastSetupTimeZone) { - Tcl_DecrRefCount(dataPtr->LastSetupTimeZone); - dataPtr->LastSetupTimeZone = NULL; - dataPtr->LastSetupTZData = NULL; - } - if (optionIndex != CLOCK_CLEAR_CACHE) { + break; + case CLOCK_SETUP_TZ: if (i+1 < objc) { - Tcl_IncrRefCount( - dataPtr->LastSetupTimeZone = timezoneObj); - } else if (dataPtr->LastUsedSetupTimeZone) { - Tcl_SetObjResult(interp, dataPtr->LastUsedSetupTimeZone); - } + if (dataPtr->AnySetupTimeZone != timezoneObj) { + Tcl_SetObjRef(dataPtr->AnySetupTimeZone, timezoneObj); + Tcl_UnsetObjRef(dataPtr->AnySetupTZData); + } + } + break; } } - } - if (optionIndex == CLOCK_CLEAR_CACHE) { - dataPtr->LastTZEpoch = 0; - dataPtr->LastUsedSetupTimeZone = NULL; + if (dataPtr->LastSetupTimeZone != NULL) { + Tcl_SetObjResult(interp, dataPtr->LastSetupTimeZone); + } + break; + case CLOCK_CLEAR_CACHE: + ClockConfigureClear(dataPtr); + break; } } @@ -541,11 +575,19 @@ ClockGetTZData( Tcl_Obj **literals = dataPtr->literals; Tcl_Obj *ret, **out = NULL; + /* if cached (if already setup this one) */ + if ( dataPtr->LastSetupTZData != NULL + && ( timezoneObj == dataPtr->LastSetupTimeZone + || timezoneObj == dataPtr->LastUnnormSetupTimeZone + ) + ) { + return dataPtr->LastSetupTZData; + } + /* differentiate GMT and system zones, because used often */ - /* simple caching, because almost used the tz-data of last timezone, (unnecessary to - * touch the refCount of it, because it is always referenced in TZData array) + /* simple caching, because almost used the tz-data of last timezone */ - if (timezoneObj == dataPtr->LastSystemTimeZone) { + if (timezoneObj == dataPtr->SystemTimeZone) { if (dataPtr->SystemSetupTZData != NULL) return dataPtr->SystemSetupTZData; out = &dataPtr->SystemSetupTZData; @@ -557,19 +599,25 @@ ClockGetTZData( out = &dataPtr->GMTSetupTZData; } else - if (timezoneObj == dataPtr->LastSetupTimeZone) { - if (dataPtr->LastSetupTZData != NULL) { - return dataPtr->LastSetupTZData; + if (timezoneObj == dataPtr->AnySetupTimeZone) { + if (dataPtr->AnySetupTZData != NULL) { + return dataPtr->AnySetupTZData; } - out = &dataPtr->LastSetupTZData; + out = &dataPtr->AnySetupTZData; } ret = Tcl_ObjGetVar2(interp, literals[LIT_TZDATA], timezoneObj, TCL_LEAVE_ERR_MSG); - /* cache using corresponding slot */ - if (ret != NULL && out != NULL) - *out = ret; + /* cache using corresponding slot and as last used */ + if (out != NULL) { + Tcl_SetObjRef(*out, ret); + } + Tcl_SetObjRef(dataPtr->LastSetupTZData, ret); + if (dataPtr->LastSetupTimeZone != timezoneObj) { + Tcl_SetObjRef(dataPtr->LastSetupTimeZone, timezoneObj); + Tcl_UnsetObjRef(dataPtr->LastUnnormSetupTimeZone); + } return ret; } /* @@ -584,9 +632,9 @@ ClockGetSystemTimeZone( Tcl_Obj **literals; /* if known (cached and same epoch) - return now */ - if (dataPtr->LastSystemTimeZone != NULL + if (dataPtr->SystemTimeZone != NULL && dataPtr->LastTZEpoch == TzsetGetEpoch()) { - return dataPtr->LastSystemTimeZone; + return dataPtr->SystemTimeZone; } literals = dataPtr->literals; @@ -610,15 +658,19 @@ ClockSetupTimeZone( Tcl_Obj *callargs[2]; /* if cached (if already setup this one) */ - if (timezoneObj == dataPtr->LastUsedSetupTimeZone) { - return timezoneObj; + if ( dataPtr->LastSetupTimeZone != NULL + && ( timezoneObj == dataPtr->LastSetupTimeZone + || timezoneObj == dataPtr->LastUnnormSetupTimeZone + ) + ) { + return dataPtr->LastSetupTimeZone; } /* differentiate GMT and system zones, because used often and already set */ timezoneObj = NormTimezoneObj(dataPtr, timezoneObj); if ( timezoneObj == dataPtr->GMTSetupTimeZone - || timezoneObj == dataPtr->LastSystemTimeZone - || timezoneObj == dataPtr->LastSetupTimeZone + || timezoneObj == dataPtr->SystemTimeZone + || timezoneObj == dataPtr->AnySetupTimeZone ) { return timezoneObj; } @@ -627,7 +679,7 @@ ClockSetupTimeZone( callargs[1] = timezoneObj; if (Tcl_EvalObjv(interp, 2, callargs, 0) == TCL_OK) { - return NormTimezoneObj(dataPtr, timezoneObj); + return dataPtr->LastSetupTimeZone; } return NULL; } @@ -743,6 +795,7 @@ ClockConvertlocaltoutcObjCmd( int created = 0; int status; + fields.tzName = NULL; /* * Check params and convert time. */ @@ -936,22 +989,6 @@ ClockGetDateFields( return TCL_OK; } - -inline -SetDateFieldsTimeZone( - TclDateFields *fields, - Tcl_Obj *timezoneObj) -{ - if (fields->tzName != timezoneObj) { - if (timezoneObj) { - Tcl_IncrRefCount(timezoneObj); - } - if (fields->tzName != NULL) { - Tcl_DecrRefCount(fields->tzName); - } - fields->tzName = timezoneObj; - } -} /* *---------------------------------------------------------------------- @@ -1030,6 +1067,8 @@ ClockGetjuliandayfromerayearmonthdayObjCmd( int status; int era = 0; + fields.tzName = NULL; + /* * Check params. */ @@ -1114,6 +1153,8 @@ ClockGetjuliandayfromerayearweekdayObjCmd( int status; int era = 0; + fields.tzName = NULL; + /* * Check params. */ @@ -1457,7 +1498,7 @@ ConvertUTCToLocalUsingTable( * Convert the time. */ - SetDateFieldsTimeZone(fields, cellv[3]); + Tcl_SetObjRef(fields->tzName, cellv[3]); fields->localSeconds = fields->seconds + fields->tzOffset; return TCL_OK; } @@ -1550,7 +1591,7 @@ ConvertUTCToLocalUsingC( if (diff > 0) { sprintf(buffer+5, "%02d", diff); } - SetDateFieldsTimeZone(fields, Tcl_NewStringObj(buffer, -1)); + Tcl_SetObjRef(fields->tzName, Tcl_NewStringObj(buffer, -1)); return TCL_OK; } @@ -1647,6 +1688,8 @@ GetYearWeekDay( TclDateFields temp; int dayOfFiscalYear; + temp.tzName = NULL; + /* * Find the given date, minus three days, plus one year. That date's * iso8601 year is an upper bound on the ISO8601 year of the given date. @@ -1863,6 +1906,8 @@ GetJulianDayFromEraYearWeekDay( * given year */ TclDateFields firstWeek; + firstWeek.tzName = NULL; + /* * Find January 4 in the ISO8601 year, which will always be in week 1. */ @@ -2557,13 +2602,16 @@ ClockScanObjCmd( if (1) { /* TODO: Tcled Scan proc - */ + int ret; Tcl_Obj *callargs[10]; memcpy(callargs, objv, objc * sizeof(*objv)); callargs[0] = Tcl_NewStringObj("::tcl::clock::__org_scan", -1); - return Tcl_EvalObjv(interp, objc, callargs, 0); + Tcl_IncrRefCount(callargs[0]); + ret = Tcl_EvalObjv(interp, objc, callargs, 0); + Tcl_DecrRefCount(callargs[0]); + return ret; } - // Tcl_SetObjResult(interp, Tcl_NewWideIntObj(10000000)); - + return TCL_OK; } @@ -2589,7 +2637,6 @@ ClockFreeScan( int secondOfDay; /* Seconds of day (time only calculation) */ Tcl_WideInt seconds; int ret = TCL_ERROR; - // Tcl_Obj *cleanUpList = Tcl_NewObj(); date.tzName = NULL; @@ -2600,7 +2647,6 @@ ClockFreeScan( if (timezoneObj == NULL) { goto done; } - // Tcl_ListObjAppendElement(NULL, cleanUpList, timezoneObj); } /* Get the data for time changes in the given zone */ @@ -2673,12 +2719,16 @@ ClockFreeScan( */ if (yy.dateHaveZone) { + Tcl_Obj *tzObjStor = NULL; int minEast = -yy.dateTimezone; int dstFlag = 1 - yy.dateDSTmode; - timezoneObj = ClockFormatNumericTimeZone( + tzObjStor = ClockFormatNumericTimeZone( 60 * minEast + 3600 * dstFlag); - // Tcl_ListObjAppendElement(NULL, cleanUpList, timezoneObj); - timezoneObj = ClockSetupTimeZone(clientData, interp, timezoneObj); + + timezoneObj = ClockSetupTimeZone(clientData, interp, tzObjStor); + if (tzObjStor != timezoneObj) { + Tcl_DecrRefCount(tzObjStor); + } if (timezoneObj == NULL) { goto done; } @@ -2695,7 +2745,7 @@ ClockFreeScan( * Assemble date, time, zone into seconds-from-epoch */ - SetDateFieldsTimeZone(&date, timezoneObj); + Tcl_SetObjRef(date.tzName, timezoneObj); if (yy.dateHaveTime == -1) { secondOfDay = 0; @@ -2853,10 +2903,7 @@ repeat_rel: done: - if (date.tzName != NULL) { - Tcl_DecrRefCount(date.tzName); - } - // Tcl_DecrRefCount(cleanUpList); + Tcl_UnsetObjRef(date.tzName); if (ret != TCL_OK) { return ret; @@ -2919,23 +2966,30 @@ ClockSecondsObjCmd( *---------------------------------------------------------------------- */ -static unsigned int +static unsigned long TzsetGetEpoch(void) { - static char* tzWas = INT2PTR(-1); /* Previous value of TZ, protected by - * clockMutex. */ - static long tzNextRefresh = 0; /* Latence before next refresh */ - static unsigned int tzWasEpoch = 1; /* Epoch, signals that TZ changed */ + static char* tzWas = INT2PTR(-1); /* Previous value of TZ, protected by + * clockMutex. */ + static long tzLastRefresh = 0; /* Used for latency before next refresh */ + static unsigned long tzWasEpoch = 0; /* Epoch, signals that TZ changed */ + static unsigned long tzEnvEpoch = 0; /* Last env epoch, for faster signaling, + that TZ changed via TCL */ - const char *tzIsNow; /* Current value of TZ */ + const char *tzIsNow; /* Current value of TZ */ - /* fast check whether environment was changed (once per second) */ + /* + * Prevent performance regression on some platforms by resolving of system time zone: + * small latency for check whether environment was changed (once per second) + * no latency if environment was chaned with tcl-env (compare both epoch values) + */ Tcl_Time now; Tcl_GetTime(&now); - if (now.sec < tzNextRefresh) { + if (now.sec == tzLastRefresh && tzEnvEpoch == TclEnvEpoch) { return tzWasEpoch; } - tzNextRefresh = now.sec + 1; + tzEnvEpoch = TclEnvEpoch; + tzLastRefresh = now.sec; /* check in lock */ Tcl_MutexLock(&clockMutex); diff --git a/generic/tclDate.h b/generic/tclDate.h index 91e677f..6ba9620 100644 --- a/generic/tclDate.h +++ b/generic/tclDate.h @@ -19,9 +19,6 @@ typedef struct DateInfo { - Tcl_Obj* messages; /* Error messages */ - const char* separatrix; /* String separating messages */ - time_t dateYear; time_t dateMonth; time_t dateDay; @@ -54,6 +51,9 @@ typedef struct DateInfo { time_t *dateRelPointer; int dateDigitCount; + + Tcl_Obj* messages; /* Error messages */ + const char* separatrix; /* String separating messages */ } DateInfo; @@ -74,4 +74,11 @@ MODULE_SCOPE time_t ToSeconds(time_t Hours, time_t Minutes, MODULE_SCOPE int TclClockFreeScan(Tcl_Interp *interp, DateInfo *info); +/* + * Other externals. + */ + +MODULE_SCOPE unsigned long TclEnvEpoch; /* Epoch of the tcl environment + * (if changed with tcl-env). */ + #endif /* _TCLCLOCK_H */ diff --git a/generic/tclEnv.c b/generic/tclEnv.c index 66ddb57..fd0a8ce 100644 --- a/generic/tclEnv.c +++ b/generic/tclEnv.c @@ -17,6 +17,10 @@ TCL_DECLARE_MUTEX(envMutex) /* To serialize access to environ. */ + +MODULE_SCOPE unsigned long TclEnvEpoch = 0; /* Epoch of the tcl environment + * (if changed with tcl-env). */ + static struct { int cacheSize; /* Number of env strings in cache. */ char **cache; /* Array containing all of the environment @@ -371,6 +375,7 @@ Tcl_PutEnv( value[0] = '\0'; TclSetEnv(name, value+1); } + TclEnvEpoch++; Tcl_DStringFree(&nameString); return 0; @@ -579,6 +584,7 @@ EnvTraceProc( if (flags & TCL_TRACE_ARRAY) { TclSetupEnv(interp); + TclEnvEpoch++; return NULL; } @@ -599,6 +605,7 @@ EnvTraceProc( value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY); TclSetEnv(name2, value); + TclEnvEpoch++; } /* @@ -622,6 +629,7 @@ EnvTraceProc( if (flags & TCL_TRACE_UNSETS) { TclUnsetEnv(name2); + TclEnvEpoch++; } return NULL; } diff --git a/library/clock.tcl b/library/clock.tcl index b4a7639..ace4176 100755 --- a/library/clock.tcl +++ b/library/clock.tcl @@ -671,7 +671,9 @@ proc ::tcl::clock::format { args } { # Get the data for time changes in the given zone if {$timezone eq ""} { - set timezone [GetSystemTimeZone] + if {[set timezone [configure -system-tz]] eq ""} { + set timezone [GetSystemTimeZone] + } } if {![info exists TZData($timezone)]} { if {[catch {SetupTimeZone $timezone} retval opts]} { @@ -1202,7 +1204,9 @@ proc ::tcl::clock::__org_scan { args } { set format {} set gmt 0 set locale c - set timezone [GetSystemTimeZone] + if {[set timezone [configure -system-tz]] eq ""} { + set timezone [GetSystemTimeZone] + } # Pick up command line options. @@ -4083,7 +4087,9 @@ proc ::tcl::clock::add { clockval args } { set offsets {} set gmt 0 set locale c - set timezone [GetSystemTimeZone] + if {[set timezone [configure -system-tz]] eq ""} { + set timezone [GetSystemTimeZone] + } foreach { a b } $args { if { [string is integer -strict $a] } { diff --git a/tests-perf/clock.perf.tcl b/tests-perf/clock.perf.tcl index 52bc91f..7ef70ce 100644 --- a/tests-perf/clock.perf.tcl +++ b/tests-perf/clock.perf.tcl @@ -15,60 +15,82 @@ # of this file. # +set ::env(TCL_TZ) :CET + +proc {**STOP**} {args} { + return -code error -level 2 "**STOP** in [info level [expr {[info level]-1}]] [join $args { }]" +} + +proc _test_get_commands {lst} { + regsub -all {(?:^|\n)[ \t]*(\#[^\n]*)(?=\n\s*[\{\#])} $lst "\n{\\1}" +} + proc test-scan {{rep 100000}} { - foreach {comment c} { - "# FreeScan : relative date" + foreach c [_test_get_commands { + # FreeScan : relative date {clock scan "5 years 18 months 385 days" -base 0 -gmt 1} - "# FreeScan : relative date with relative weekday" + # FreeScan : relative date with relative weekday {clock scan "5 years 18 months 385 days Fri" -base 0 -gmt 1} - "# FreeScan : relative date with ordinal month" + # FreeScan : relative date with ordinal month {clock scan "5 years 18 months 385 days next 1 January" -base 0 -gmt 1} - "# FreeScan : relative date with ordinal month and relative weekday" + # FreeScan : relative date with ordinal month and relative weekday {clock scan "5 years 18 months 385 days next January Fri" -base 0 -gmt 1} - "# FreeScan : ordinal month" + # FreeScan : ordinal month {clock scan "next January" -base 0 -gmt 1} - "# FreeScan : relative week" + # FreeScan : relative week {clock scan "next Fri" -base 0 -gmt 1} - "# FreeScan : relative weekday and week offset " + # FreeScan : relative weekday and week offset {clock scan "next January + 2 week" -base 0 -gmt 1} - "# FreeScan : time only with base" + # FreeScan : time only with base {clock scan "19:18:30" -base 148863600 -gmt 1} - "# FreeScan : time only without base" + # FreeScan : time only without base, gmt {clock scan "19:18:30" -gmt 1} - "# FreeScan : date, system time zone" + # FreeScan : time only without base, system + {clock scan "19:18:30"} + # FreeScan : date, system time zone {clock scan "05/08/2016 20:18:30"} - "# FreeScan : date, supplied time zone" + # FreeScan : date, supplied time zone {clock scan "05/08/2016 20:18:30" -timezone :CET} - "# FreeScan : date, supplied gmt (equivalent -timezone :GMT)" + # FreeScan : date, supplied gmt (equivalent -timezone :GMT) {clock scan "05/08/2016 20:18:30" -gmt 1} - "# FreeScan : time only, numeric zone in string, base time gmt (exchange zones between gmt / -0500)" + # FreeScan : date, supplied time zone gmt + {clock scan "05/08/2016 20:18:30" -timezone :GMT} + # FreeScan : time only, numeric zone in string, base time gmt (exchange zones between gmt / -0500) {clock scan "20:18:30 -0500" -base 148863600 -gmt 1} - "# FreeScan : time only, zone in string (exchange zones between system / gmt)" + # FreeScan : time only, zone in string (exchange zones between system / gmt) {clock scan "19:18:30 GMT" -base 148863600} - } { - if {[string first "**STOP**" $comment] != -1} { return -code error "**STOP**" } - puts "\n% $comment\n% $c" - puts [clock format [{*}$c] -locale en] + # FreeScan : fast switch of zones in cycle - GMT, MST, CET (system) and EST + {clock scan "19:18:30 MST" -base 148863600 -gmt 1 + clock scan "19:18:30 EST" -base 148863600 + } + }] { + puts "% [regsub -all {\n[ \t]*} $c {; }]" + if {[regexp {\s*\#} $c]} continue + puts [clock format [if 1 $c] -locale en] puts [time $c $rep] + puts "" } } proc test-other {{rep 100000}} { - foreach {comment c} { - "# Bad zone" - {catch {clock scan "1 day" -timezone BAD_ZONE}} - } { - if {[string first "**STOP**" $comment] != -1} { return -code error "**STOP**" } - puts "\n% $comment\n% $c" + foreach c [_test_get_commands { + # Bad zone + {catch {clock scan "1 day" -timezone BAD_ZONE -locale en}} + }] { + puts "% [regsub -all {\n[ \t]*} $c {; }]" + if {[regexp {\s*\#} $c]} continue puts [if 1 $c] puts [time $c $rep] + puts "" } } -set factor 10; # 50 -if 1 {;# +proc test {factor} { + puts "" test-scan [expr 10000 * $factor] test-other [expr 5000 * $factor] puts \n**OK** -};# \ No newline at end of file +} + +test 20; # 50 diff --git a/tests/clock.test b/tests/clock.test index ecc6f5f..477f142 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -35803,6 +35803,27 @@ test clock-34.40 {clock scan, next day of week} { clock format [clock scan "next thursday" -base [clock scan 20000112]] \ -format {%b %d, %Y} } "Jan 20, 2000" +test clock-34.40.1 {clock scan, ordinal month after relative date} { + # This will fail without the bug fix (clock.tcl), as still missing + # month/julian day conversion before ordinal month increment + clock format [ \ + clock scan "5 years 18 months 387 days" -base 0 -gmt 1 + ] -format {%a, %b %d, %Y} -gmt 1 -locale en_US_roman +} "Sat, Jul 23, 1977" +test clock-34.40.2 {clock scan, ordinal month after relative date} { + # This will fail without the bug fix (clock.tcl), as still missing + # month/julian day conversion before ordinal month increment + clock format [ \ + clock scan "5 years 18 months 387 days next Jan" -base 0 -gmt 1 + ] -format {%a, %b %d, %Y} -gmt 1 -locale en_US_roman +} "Mon, Jan 23, 1978" +test clock-34.40.3 {clock scan, day of week after ordinal date} { + # This will fail without the bug fix (clock.tcl), because the relative + # week day should be applied after whole date conversion + clock format [ \ + clock scan "5 years 18 months 387 days next January Fri" -base 0 -gmt 1 + ] -format {%a, %b %d, %Y} -gmt 1 -locale en_US_roman +} "Fri, Jan 27, 1978" # weekday specification and base. test clock-34.41 {2nd monday in november} { -- cgit v0.12