summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/clock.n13
-rw-r--r--generic/tclEvent.c3
-rw-r--r--generic/tclExecute.c27
-rw-r--r--generic/tclInt.h1
-rw-r--r--generic/tclZlib.c9
-rwxr-xr-xlibrary/clock.tcl60
-rw-r--r--tests/clock.test55
-rw-r--r--tests/zlib.test18
-rw-r--r--unix/tclUnixThrd.c25
-rw-r--r--win/coffbase.txt1
-rw-r--r--win/tclWinNotify.c26
-rw-r--r--win/tclWinThrd.c33
12 files changed, 204 insertions, 67 deletions
diff --git a/doc/clock.n b/doc/clock.n
index 889a5da..ac50e36 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 - 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)
diff --git a/generic/tclEvent.c b/generic/tclEvent.c
index 8305410..a16a3b1 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)
+ TclpInitAllocCache();
+#endif
#ifdef TCL_MEM_DEBUG
TclInitDbCkalloc(); /* Process wide mutex init */
#endif
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index b3a8d8e..5fcde79 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -35,14 +35,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
@@ -2115,8 +2115,14 @@ TEBCresume(
* sporadically: no special need for speed.
*/
- int instructionCount = 0; /* Counter that is used to work out when to
- * call Tcl_AsyncReady() */
+ unsigned interruptCounter = 1;
+ /* Counter that is used to work out when to
+ * call Tcl_AsyncReady(). This must be 1
+ * initially so that we call the async-check
+ * stanza early, otherwise there are command
+ * sequences that can make the interpreter
+ * busy-loop without an opportunity to
+ * recognise an interrupt. */
const char *curInstName;
#ifdef TCL_COMPILE_DEBUG
int traceInstructions; /* Whether we are doing instruction-level
@@ -2314,10 +2320,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 ((--interruptCounter) == 0) {
+ interruptCounter = ASYNC_CHECK_COUNT;
DECACHE_STACK_INFO();
if (TclAsyncReady(iPtr)) {
result = Tcl_AsyncInvoke(interp, result);
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 2de0046..e8eba7a 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -4085,6 +4085,7 @@ MODULE_SCOPE void TclFreeAllocCache(void *);
MODULE_SCOPE void * TclpGetAllocCache(void);
MODULE_SCOPE void TclpSetAllocCache(void *);
MODULE_SCOPE void TclpFreeAllocMutex(Tcl_Mutex *mutex);
+MODULE_SCOPE void TclpInitAllocCache(void);
MODULE_SCOPE void TclpFreeAllocCache(void *);
/*
diff --git a/generic/tclZlib.c b/generic/tclZlib.c
index 50d9a30..691d57a 100644
--- a/generic/tclZlib.c
+++ b/generic/tclZlib.c
@@ -1194,11 +1194,12 @@ Tcl_ZlibStreamPut(
zshPtr->stream.next_out = (Bytef *) dataTmp;
e = deflate(&zshPtr->stream, flush);
- while (e == Z_BUF_ERROR) {
+ while (e == Z_BUF_ERROR || (flush == Z_FINISH && e == Z_OK)) {
/*
- * Output buffer too small to hold the data being generated; so
- * put a new buffer into place after saving the old generated
- * data to the outData list.
+ * Output buffer too small to hold the data being generated or we
+ * are doing the end-of-stream flush (which can spit out masses of
+ * data). This means we need to put a new buffer into place after
+ * saving the old generated data to the outData list.
*/
obj = Tcl_NewByteArrayObj((unsigned char *) dataTmp, outSize);
diff --git a/library/clock.tcl b/library/clock.tcl
index 8e4b657..535a67d 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,56 @@ 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 } {
+
+ if {$days == 0} {
+ return $clockval
+ }
+
+ 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 b2ccdf2..08036ca 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,57 @@ 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 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 and back} -body {
+ set n [clock seconds]
+ # 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"
+} -result {OK}
+
+# END testcases30
+
test clock-31.1 {system locale} \
-constraints win \
diff --git a/tests/zlib.test b/tests/zlib.test
index 7a486ba..968469d 100644
--- a/tests/zlib.test
+++ b/tests/zlib.test
@@ -875,6 +875,24 @@ test zlib-11.3 {Bug 3595576 variant} -setup {
} -cleanup {
removeFile $file
} -returnCodes error -result {can't set "noSuchNs::foo": parent namespace doesn't exist}
+
+test zlib-12.1 {Tk Bug 9eb55debc5} -constraints zlib -setup {
+ set stream [zlib stream compress]
+} -body {
+ for {set opts {};set y 0} {$y < 60} {incr y} {
+ for {set line {};set x 0} {$x < 100} {incr x} {
+ append line [binary format ccc $x $y 128]
+ }
+ if {$y == 59} {
+ set opts -finalize
+ }
+ $stream put {*}$opts $line
+ }
+ set data [$stream get]
+ list [string length $data] [string length [zlib decompress $data]]
+} -cleanup {
+ $stream close
+} -result {12026 18000}
::tcltest::cleanupTests
return
diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c
index 554a2dc..cf81850 100644
--- a/unix/tclUnixThrd.c
+++ b/unix/tclUnixThrd.c
@@ -673,7 +673,6 @@ TclpInetNtoa(
*/
#ifdef USE_THREAD_ALLOC
-static volatile int initialized = 0;
static pthread_key_t key;
typedef struct allocMutex {
@@ -710,6 +709,14 @@ TclpFreeAllocMutex(
}
void
+TclpInitAllocCache(void)
+{
+ pthread_mutex_lock(allocLockPtr);
+ pthread_key_create(&key, TclpFreeAllocCache);
+ pthread_mutex_unlock(allocLockPtr);
+}
+
+void
TclpFreeAllocCache(
void *ptr)
{
@@ -721,28 +728,14 @@ 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 *
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/coffbase.txt b/win/coffbase.txt
index 0ebe18a..3314f26 100644
--- a/win/coffbase.txt
+++ b/win/coffbase.txt
@@ -34,6 +34,7 @@ tclsdl 0x10B20000 0x00080000
vqtcl 0x10C00000 0x00010000
tdbc 0x10C40000 0x00010000
thread 0x10C80000 0x00020000
+nsf 0x10ca0000 0x00080000
;
; insert new packages here
;
diff --git a/win/tclWinNotify.c b/win/tclWinNotify.c
index ea4035b..1ad022d 100644
--- a/win/tclWinNotify.c
+++ b/win/tclWinNotify.c
@@ -51,7 +51,8 @@ static Tcl_ThreadDataKey dataKey;
static int notifierCount = 0;
static const TCHAR className[] = TEXT("TclNotifier");
-TCL_DECLARE_MUTEX(notifierMutex)
+static int initialized = 0;
+static CRITICAL_SECTION notifierMutex;
/*
* Static routines defined in this file.
@@ -85,12 +86,19 @@ Tcl_InitNotifier(void)
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
WNDCLASS class;
+ TclpMasterLock();
+ if (!initialized) {
+ initialized = 1;
+ InitializeCriticalSection(&notifierMutex);
+ }
+ TclpMasterUnlock();
+
/*
* Register Notifier window class if this is the first thread to use
* this module.
*/
- Tcl_MutexLock(&notifierMutex);
+ EnterCriticalSection(&notifierMutex);
if (notifierCount == 0) {
class.style = 0;
class.cbClsExtra = 0;
@@ -108,7 +116,7 @@ Tcl_InitNotifier(void)
}
}
notifierCount++;
- Tcl_MutexUnlock(&notifierMutex);
+ LeaveCriticalSection(&notifierMutex);
tsdPtr->pending = 0;
tsdPtr->timerActive = 0;
@@ -183,12 +191,14 @@ Tcl_FinalizeNotifier(
* notifier window class.
*/
- Tcl_MutexLock(&notifierMutex);
- notifierCount--;
- if (notifierCount == 0) {
- UnregisterClass(className, TclWinGetTclInstance());
+ EnterCriticalSection(&notifierMutex);
+ if (notifierCount) {
+ notifierCount--;
+ if (notifierCount == 0) {
+ UnregisterClass(className, TclWinGetTclInstance());
+ }
}
- Tcl_MutexUnlock(&notifierMutex);
+ LeaveCriticalSection(&notifierMutex);
}
}
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c
index e44363b..987734d 100644
--- a/win/tclWinThrd.c
+++ b/win/tclWinThrd.c
@@ -119,7 +119,6 @@ typedef struct WinCondition {
*/
#ifdef USE_THREAD_ALLOC
-static int once;
static DWORD tlsKey;
typedef struct allocMutex {
@@ -968,24 +967,24 @@ TclpFreeAllocMutex(
free(lockPtr);
}
-void *
-TclpGetAllocCache(void)
+void
+TclpInitAllocCache(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.
- */
+ /*
+ * 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");
- }
+ tlsKey = TlsAlloc();
+ if (tlsKey == TLS_OUT_OF_INDEXES) {
+ Tcl_Panic("could not allocate thread local storage");
}
+}
+void *
+TclpGetAllocCache(void)
+{
+ void *result;
result = TlsGetValue(tlsKey);
if ((result == NULL) && (GetLastError() != NO_ERROR)) {
Tcl_Panic("TlsGetValue failed from TclpGetAllocCache");
@@ -1021,7 +1020,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()
@@ -1031,9 +1030,7 @@ TclpFreeAllocCache(
if (!success) {
Tcl_Panic("TlsFree failed from TclpFreeAllocCache");
}
- once = 0; /* reset for next time. */
}
-
}
#endif /* USE_THREAD_ALLOC */