From 8f8aa2b0d95a23c9d921ef02b59869e4933e7b59 Mon Sep 17 00:00:00 2001 From: Kevin B Kenny Date: Wed, 21 Oct 2015 23:30:41 +0000 Subject: Micro-optimization: remove double checked lock from TclGetAllocCache in favour of initialization in TclInitSubsystems --- generic/tclEvent.c | 3 +++ generic/tclInt.h | 3 +++ unix/tclUnixThrd.c | 27 ++++++++++----------------- win/tclWinThrd.c | 33 +++++++++++++++------------------ 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/generic/tclEvent.c b/generic/tclEvent.c index 8305410..d62850b 100644 --- a/generic/tclEvent.c +++ b/generic/tclEvent.c @@ -1043,6 +1043,9 @@ TclInitSubsystems(void) #if USE_TCLALLOC TclInitAlloc(); /* Process wide mutex init */ #endif +#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) + TclpInitThreadAlloc(); +#endif #ifdef TCL_MEM_DEBUG TclInitDbCkalloc(); /* Process wide mutex init */ #endif diff --git a/generic/tclInt.h b/generic/tclInt.h index 356d250..d4baed4 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -3146,6 +3146,9 @@ MODULE_SCOPE int TclpLoadMemory(Tcl_Interp *interp, void *buffer, Tcl_FSUnloadFileProc **unloadProcPtr, int flags); #endif MODULE_SCOPE void TclInitThreadStorage(void); +#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) +MODULE_SCOPE void TclpInitThreadAlloc(void); +#endif MODULE_SCOPE void TclFinalizeThreadDataThread(void); MODULE_SCOPE void TclFinalizeThreadStorage(void); #ifdef TCL_WIDE_CLICKS diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c index ea03332..0f4a8a3 100644 --- a/unix/tclUnixThrd.c +++ b/unix/tclUnixThrd.c @@ -680,7 +680,6 @@ TclpInetNtoa( */ #ifdef USE_THREAD_ALLOC -static volatile int initialized = 0; static pthread_key_t key; typedef struct allocMutex { @@ -727,29 +726,23 @@ TclpFreeAllocCache( TclFreeAllocCache(ptr); pthread_setspecific(key, NULL); - - } else if (initialized) { - /* - * Called by us in TclFinalizeThreadAlloc() during the library - * finalization initiated from Tcl_Finalize() - */ - + + } else { pthread_key_delete(key); - initialized = 0; } } +void +TclpInitThreadAlloc(void) +{ + pthread_mutex_lock(allocLockPtr); + pthread_key_create(&key, TclpFreeAllocCache); + pthread_mutex_unlock(allocLockPtr); +} + void * TclpGetAllocCache(void) { - if (!initialized) { - pthread_mutex_lock(allocLockPtr); - if (!initialized) { - pthread_key_create(&key, TclpFreeAllocCache); - initialized = 1; - } - pthread_mutex_unlock(allocLockPtr); - } return pthread_getspecific(key); } diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index 1c9d483..fac8ab3 100644 --- a/win/tclWinThrd.c +++ b/win/tclWinThrd.c @@ -122,7 +122,6 @@ typedef struct WinCondition { */ #ifdef USE_THREAD_ALLOC -static int once; static DWORD tlsKey; typedef struct allocMutex { @@ -971,24 +970,24 @@ TclpFreeAllocMutex( free(lockPtr); } +void +TclInitThreadAlloc(void) +{ + /* + * We need to make sure that TclpFreeAllocCache is called on each + * thread that calls this, but only on threads that call this. + */ + + tlsKey = TlsAlloc(); + if (tlsKey == TLS_OUT_OF_INDEXES) { + Tcl_Panic("could not allocate thread local storage"); + } +} + void * TclpGetAllocCache(void) { void *result; - - if (!once) { - /* - * We need to make sure that TclpFreeAllocCache is called on each - * thread that calls this, but only on threads that call this. - */ - - tlsKey = TlsAlloc(); - once = 1; - if (tlsKey == TLS_OUT_OF_INDEXES) { - Tcl_Panic("could not allocate thread local storage"); - } - } - result = TlsGetValue(tlsKey); if ((result == NULL) && (GetLastError() != NO_ERROR)) { Tcl_Panic("TlsGetValue failed from TclpGetAllocCache"); @@ -1024,7 +1023,7 @@ TclpFreeAllocCache( if (!success) { Tcl_Panic("TlsSetValue failed from TclpFreeAllocCache"); } - } else if (once) { + } else { /* * Called by us in TclFinalizeThreadAlloc() during the library * finalization initiated from Tcl_Finalize() @@ -1034,9 +1033,7 @@ TclpFreeAllocCache( if (!success) { Tcl_Panic("TlsFree failed from TclpFreeAllocCache"); } - once = 0; /* reset for next time. */ } - } #endif /* USE_THREAD_ALLOC */ -- cgit v0.12 From 30c0c8a7feaadda0755370a64f49f2db1a101dce Mon Sep 17 00:00:00 2001 From: dkf Date: Thu, 22 Oct 2015 01:00:28 +0000 Subject: Turn off NRE asserts by default. About a 5% speedup on [clock format]. --- generic/tclBasic.c | 3 --- generic/tclExecute.c | 3 --- generic/tclInt.h | 4 +++- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index a09bf10..5c5bc64 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -22,10 +22,7 @@ #include "tclCompile.h" #include "tommath.h" #include - -#if NRE_ENABLE_ASSERTS #include -#endif #define INTERP_STACK_INITIAL_SIZE 2000 #define CORO_STACK_INITIAL_SIZE 200 diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 7f65262..b10af65 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -20,10 +20,7 @@ #include "tclOOInt.h" #include "tommath.h" #include - -#if NRE_ENABLE_ASSERTS #include -#endif /* * Hack to determine whether we may expect IEEE floating point. The hack is diff --git a/generic/tclInt.h b/generic/tclInt.h index d4baed4..50eb370 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -4799,7 +4799,9 @@ void Tcl_Panic(const char *, ...) __attribute__((analyzer_noreturn)); */ #define NRE_USE_SMALL_ALLOC 1 /* Only turn off for debugging purposes. */ -#define NRE_ENABLE_ASSERTS 1 +#ifndef NRE_ENABLE_ASSERTS +#define NRE_ENABLE_ASSERTS 0 +#endif /* * This is the main data struct for representing NR commands. It is designed -- cgit v0.12 From 56619f9f704125177255b5d995f9f64228f81ca5 Mon Sep 17 00:00:00 2001 From: Kevin B Kenny Date: Thu, 22 Oct 2015 14:07:01 +0000 Subject: fix typo in micro-optimization TclpInitThreadAlloc --- win/tclWinThrd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index fac8ab3..a2fc226 100644 --- a/win/tclWinThrd.c +++ b/win/tclWinThrd.c @@ -971,7 +971,7 @@ TclpFreeAllocMutex( } void -TclInitThreadAlloc(void) +TclpInitThreadAlloc(void) { /* * We need to make sure that TclpFreeAllocCache is called on each -- cgit v0.12 From c0946d430ecf115767a1e029ed211ed865a012c0 Mon Sep 17 00:00:00 2001 From: dkf Date: Fri, 23 Oct 2015 21:52:10 +0000 Subject: Knock perhaps 1% off execution time: guard on TclAsyncReady more efficient when decrementing to zero. --- generic/tclExecute.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/generic/tclExecute.c b/generic/tclExecute.c index b10af65..f6dfc46 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -34,14 +34,14 @@ #endif /* - * A mask (should be 2**n-1) that is used to work out when the bytecode engine - * should call Tcl_AsyncReady() to see whether there is a signal that needs - * handling. + * A counter that is used to work out when the bytecode engine should call + * Tcl_AsyncReady() to see whether there is a signal that needs handling, and + * other expensive periodic operations. */ -#ifndef ASYNC_CHECK_COUNT_MASK -# define ASYNC_CHECK_COUNT_MASK 63 -#endif /* !ASYNC_CHECK_COUNT_MASK */ +#ifndef ASYNC_CHECK_COUNT +# define ASYNC_CHECK_COUNT 64 +#endif /* !ASYNC_CHECK_COUNT */ /* * Boolean flag indicating whether the Tcl bytecode interpreter has been @@ -2116,7 +2116,8 @@ TEBCresume( * sporadically: no special need for speed. */ - int instructionCount = 0; /* Counter that is used to work out when to + int instructionCount = ASYNC_CHECK_COUNT; + /* Counter that is used to work out when to * call Tcl_AsyncReady() */ const char *curInstName; #ifdef TCL_COMPILE_DEBUG @@ -2315,10 +2316,11 @@ TEBCresume( /* * Check for asynchronous handlers [Bug 746722]; we do the check every - * ASYNC_CHECK_COUNT_MASK instruction, of the form (2**n-1). + * ASYNC_CHECK_COUNT instructions. */ - if ((instructionCount++ & ASYNC_CHECK_COUNT_MASK) == 0) { + if (!(--instructionCount)) { + instructionCount = ASYNC_CHECK_COUNT; DECACHE_STACK_INFO(); if (TclAsyncReady(iPtr)) { result = Tcl_AsyncInvoke(interp, result); -- cgit v0.12 From d6eeb008f739a45e62dc3719510a389454bf464b Mon Sep 17 00:00:00 2001 From: gahr Date: Wed, 17 Feb 2016 11:03:48 +0000 Subject: [5f71353740] Support the "weekdays" unit in [clock add] --- doc/clock.n | 13 ++++++------- library/clock.tcl | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++---- tests/clock.test | 37 ++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/doc/clock.n b/doc/clock.n index 889a5da..53db33e 100644 --- a/doc/clock.n +++ b/doc/clock.n @@ -89,10 +89,9 @@ have 59 or 61 seconds. .TP \fIunit\fR One of the words, \fBseconds\fR, \fBminutes\fR, \fBhours\fR, -\fBdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR, or -any unique prefix of such a word. Used in conjunction with \fIcount\fR -to identify an interval of time, for example, \fI3 seconds\fR or -\fI1 year\fR. +\fBdays\fR, \fBweekdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR. +Used in conjunction with \fIcount\fR to identify an interval of time, +for example, \fI3 seconds\fR or \fI1 year\fR. .SS "OPTIONS" .TP \fB\-base\fR time @@ -175,8 +174,7 @@ given as its first argument. The remaining arguments (other than the possible \fB\-timezone\fR, \fB\-locale\fR and \fB\-gmt\fR options) are integers and keywords in alternation, where the keywords are chosen from \fBseconds\fR, \fBminutes\fR, \fBhours\fR, -\fBdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR, or -any unique prefix of such a word. +\fBdays\fR, \fBweekdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR. .PP Addition of seconds, minutes and hours is fairly straightforward; the given time increment (times sixty for minutes, or 3600 for hours) @@ -213,7 +211,8 @@ the given time to a calendar day and time of day in the appropriate time zone and locale. The requisite number of days (weeks are converted to days by multiplying by seven) is added to the calendar day, and the date and time are then converted back to a count of seconds from -the epoch time. +the epoch time. The \fBweekdays\fR keyword is similar to \fBdays\fR, +with the only difference that weekends are skipped. .PP Adding and subtracting a given number of days across the point that the time changes at the start or end of summer time (Daylight Saving Time) diff --git a/library/clock.tcl b/library/clock.tcl index 8e4b657..231f1ae 100755 --- a/library/clock.tcl +++ b/library/clock.tcl @@ -4248,7 +4248,7 @@ proc ::tcl::clock::add { clockval args } { ?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE?\"" } if { [catch { expr {wide($clockval)} } result] } { - return -code error $result + return -code error "expected integer but got \"$clockval\"" } set offsets {} @@ -4287,9 +4287,6 @@ proc ::tcl::clock::add { clockval args } { -errorcode [list CLOCK gmtWithTimezone] \ "cannot use -gmt and -timezone in same call" } - if { [catch { expr { wide($clockval) } } result] } { - return -code error "expected integer but got \"$clockval\"" - } if { ![string is boolean -strict $gmt] } { return -code error "expected boolean value but got \"$gmt\"" } elseif { $gmt } { @@ -4326,6 +4323,11 @@ proc ::tcl::clock::add { clockval args } { $changeover] } + weekdays - weekday { + set clockval [AddWeekDays $quantity $clockval $timezone \ + $changeover] + } + hours - hour { set clockval [expr { 3600 * $quantity + $clockval }] } @@ -4425,6 +4427,52 @@ proc ::tcl::clock::AddMonths { months clockval timezone changeover } { #---------------------------------------------------------------------- # +# AddWeekDays -- +# +# Add a given number of week days (skipping Saturdays and Sundays) +# to a given clock value in a given time zone. +# +# Parameters: +# days - Number of days to add (may be negative) +# clockval - Seconds since the epoch before the operation +# timezone - Time zone in which the operation is to be performed +# changeover - Julian Day on which the Gregorian calendar was adopted +# in the target locale. +# +# Results: +# Returns the new clock value as a number of seconds since the epoch. +# +# Side effects: +# None. +# +#---------------------------------------------------------------------- + +proc ::tcl::clock::AddWeekDays { days clockval timezone changeover } { + + set day [format $clockval -format %u] + + set weeks [expr {$days / 5}] + set rdays [expr {$days % 5}] + set toAdd [expr {7 * $weeks + $rdays}] + set resDay [expr {$day + ($toAdd % 7)}] + + # Adjust if we start from a weekend + if {$day > 5} { + set adj [expr {5 - $day}] + incr toAdd $adj + incr resDay $adj + } + + # Adjust if we end up on a weekend + if {$resDay > 5} { + incr toAdd 2 + } + + AddDays $toAdd $clockval $timezone $changeover +} + +#---------------------------------------------------------------------- +# # AddDays -- # # Add a given number of days to a given clock value in a given time diff --git a/tests/clock.test b/tests/clock.test index 615f3a8..fc7992d 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -34992,6 +34992,10 @@ test clock-29.1800 {time parsing} { } 86399 # END testcases29 + +# BEGIN testcases30 + +# Test [clock add] test clock-30.1 {clock add years} { set t [clock scan 2000-01-01 -format %Y-%m-%d -timezone :UTC] set f [clock add $t 1 year -timezone :UTC] @@ -35218,6 +35222,39 @@ test clock-30.25 {clock add seconds at DST conversion} { set x1 [clock format $f1 -format {%Y-%m-%d %H:%M:%S %z} \ -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00] } {2004-10-31 01:00:00 -0500} +test clock-30.26 {clock add weekdays} { + set t [clock scan {2013-11-20}] ;# Wednesday + set f1 [clock add $t 3 weekdays] + set x1 [clock format $f1 -format {%Y-%m-%d}] +} {2013-11-25} +test clock-30.27 {clock add weekdays starting on Saturday} { + set t [clock scan {2013-11-23}] ;# Saturday + set f1 [clock add $t 1 weekday] + set x1 [clock format $f1 -format {%Y-%m-%d}] +} {2013-11-25} +test clock-30.28 {clock add weekdays starting on Sunday} { + set t [clock scan {2013-11-24}] ;# Sunday + set f1 [clock add $t 1 weekday] + set x1 [clock format $f1 -format {%Y-%m-%d}] +} {2013-11-25} +test clock-30.29 {clock add weekdays systematic} -body { + set n [clock seconds] + set d [clock format $n -format %u] + for {set i 1} {$i < 100} {incr i} { + set res_no [clock format [clock add $n $i weekdays] -format %u] + set exp_mod [expr {($d+$i)%5}] + if {$exp_mod == 0} { + set exp_mod 5 + } + if {$res_no != $exp_mod} { + return "Got $res_no adding $i to $n, expected: $exp_mod" + } + } + return "OK" +} -result {OK} + +# END testcases30 + test clock-31.1 {system locale} \ -constraints win \ -- cgit v0.12 From 35844385d63395c887098709940734d55556397a Mon Sep 17 00:00:00 2001 From: gahr Date: Tue, 23 Feb 2016 16:31:18 +0000 Subject: Make sure that adding 0 weekdays doesn't result in going back in time --- library/clock.tcl | 4 ++++ tests/clock.test | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/library/clock.tcl b/library/clock.tcl index 231f1ae..535a67d 100755 --- a/library/clock.tcl +++ b/library/clock.tcl @@ -4449,6 +4449,10 @@ proc ::tcl::clock::AddMonths { months clockval timezone changeover } { proc ::tcl::clock::AddWeekDays { days clockval timezone changeover } { + if {$days == 0} { + return $clockval + } + set day [format $clockval -format %u] set weeks [expr {$days / 5}] diff --git a/tests/clock.test b/tests/clock.test index fc7992d..0b26213 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -35237,7 +35237,12 @@ test clock-30.28 {clock add weekdays starting on Sunday} { set f1 [clock add $t 1 weekday] set x1 [clock format $f1 -format {%Y-%m-%d}] } {2013-11-25} -test clock-30.29 {clock add weekdays systematic} -body { +test clock-30.29 {clock add 0 weekdays starting on a weekend} { + set t [clock scan {2016-02-27}] ;# Saturday + set f1 [clock add $t 0 weekdays] + set x1 [clock format $f1 -format {%Y-%m-%d}] +} {2016-02-27} +test clock-30.30 {clock add weekdays systematic} -body { set n [clock seconds] set d [clock format $n -format %u] for {set i 1} {$i < 100} {incr i} { -- cgit v0.12 From 013f304b822f74d59361802adb97a6ccc4b7adbd Mon Sep 17 00:00:00 2001 From: gahr Date: Wed, 24 Feb 2016 09:10:01 +0000 Subject: More comprehensive tests adding and subtracting week-days --- tests/clock.test | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/tests/clock.test b/tests/clock.test index 0b26213..42285a8 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -35242,17 +35242,30 @@ test clock-30.29 {clock add 0 weekdays starting on a weekend} { set f1 [clock add $t 0 weekdays] set x1 [clock format $f1 -format {%Y-%m-%d}] } {2016-02-27} -test clock-30.30 {clock add weekdays systematic} -body { +test clock-30.30 {clock add weekdays and back} -body { set n [clock seconds] - set d [clock format $n -format %u] - for {set i 1} {$i < 100} {incr i} { - set res_no [clock format [clock add $n $i weekdays] -format %u] - set exp_mod [expr {($d+$i)%5}] - if {$exp_mod == 0} { - set exp_mod 5 - } - if {$res_no != $exp_mod} { - return "Got $res_no adding $i to $n, expected: $exp_mod" + # we start on each day of the week + for {set i 0} {$i < 7} {incr i} { + set start [clock add $n $i days] + set startu [clock format $start -format %u] + # add 0 - 100 weekdays + for {set j 0} {$j < 100} {incr j} { + set forth [clock add $start $j weekdays] + set back [clock add $forth -$j weekdays] + # If $s was a weekday or $j was 0, $b must be the same day. + # Otherwise, $b must be the immediately preceeding Friday + set fail 0 + if {$j == 0 || $startu < 6} { + if {$start != $back} { set fail 1} + } else { + set friday [clock add $start -[expr {$startu % 5}] days] + if {$friday != $back} { set fail 1 } + } + if {$fail} { + set sdate [clock format $start -format {%Y-%m-%d}] + set bdate [clock format $back -format {%Y-%m-%d}] + return "$sdate + $j - $j := $bdate" + } } } return "OK" -- cgit v0.12 From 08296be4e452195b4d61373e46c0e07d96b1b32c Mon Sep 17 00:00:00 2001 From: gahr Date: Thu, 3 Mar 2016 08:30:56 +0000 Subject: Clarify that weekends are intended as Saturdays and Sundays --- doc/clock.n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/clock.n b/doc/clock.n index 53db33e..ac50e36 100644 --- a/doc/clock.n +++ b/doc/clock.n @@ -212,7 +212,7 @@ time zone and locale. The requisite number of days (weeks are converted to days by multiplying by seven) is added to the calendar day, and the date and time are then converted back to a count of seconds from the epoch time. The \fBweekdays\fR keyword is similar to \fBdays\fR, -with the only difference that weekends are skipped. +with the only difference that weekends - Saturdays and Sundays - are skipped. .PP Adding and subtracting a given number of days across the point that the time changes at the start or end of summer time (Daylight Saving Time) -- cgit v0.12