summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordkf <donal.k.fellows@manchester.ac.uk>2016-05-16 10:57:36 (GMT)
committerdkf <donal.k.fellows@manchester.ac.uk>2016-05-16 10:57:36 (GMT)
commitdc3841fe45692ab1cda05625f74d951a5a0460d7 (patch)
tree3cdc9cfdcff3462e5db72d81242a011836a7f444
parent09ec4a59520b6557298cffe32e679a6733928077 (diff)
downloadtcl-dc3841fe45692ab1cda05625f74d951a5a0460d7.zip
tcl-dc3841fe45692ab1cda05625f74d951a5a0460d7.tar.gz
tcl-dc3841fe45692ab1cda05625f74d951a5a0460d7.tar.bz2
Possible fix for [f97d4ee020]; uses a two-stage approach to avoid quadratic behaviour.
-rw-r--r--generic/tclNamesp.c76
1 files changed, 53 insertions, 23 deletions
diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c
index dfab185..d286de4 100644
--- a/generic/tclNamesp.c
+++ b/generic/tclNamesp.c
@@ -1105,8 +1105,6 @@ TclTeardownNamespace(
Interp *iPtr = (Interp *) nsPtr->interp;
register Tcl_HashEntry *entryPtr;
Tcl_HashSearch search;
- Tcl_Namespace *childNsPtr;
- Tcl_Command cmd;
int i;
/*
@@ -1121,16 +1119,27 @@ TclTeardownNamespace(
/*
* Delete all commands in this namespace. Be careful when traversing the
* hash table: when each command is deleted, it removes itself from the
- * command table.
- *
- * Don't optimize to Tcl_NextHashEntry() because of traces.
+ * command table. Because of traces (and the desire to avoid the quadratic
+ * problems of just using Tcl_FirstHashEntry over and over, [Bug
+ * f97d4ee020]) we copy to a temporary array and then delete all those
+ * commands.
*/
- for (entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
- entryPtr != NULL;
- entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search)) {
- cmd = Tcl_GetHashValue(entryPtr);
- Tcl_DeleteCommandFromToken((Tcl_Interp *) iPtr, cmd);
+ while (nsPtr->cmdTable.numEntries > 0) {
+ int length = nsPtr->cmdTable.numEntries;
+ Tcl_Command *cmds = TclStackAlloc((Tcl_Interp *) iPtr,
+ sizeof(Tcl_Command) * length);
+
+ i = 0;
+ for (entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
+ entryPtr != NULL;
+ entryPtr = Tcl_NextHashEntry(&search)) {
+ cmds[i++] = Tcl_GetHashValue(entryPtr);
+ }
+ for (i = 0 ; i < length ; i++) {
+ Tcl_DeleteCommandFromToken((Tcl_Interp *) iPtr, cmds[i]);
+ }
+ TclStackFree((Tcl_Interp *) iPtr, cmds);
}
Tcl_DeleteHashTable(&nsPtr->cmdTable);
Tcl_InitHashTable(&nsPtr->cmdTable, TCL_STRING_KEYS);
@@ -1175,25 +1184,46 @@ TclTeardownNamespace(
*
* BE CAREFUL: When each child is deleted, it will divorce itself from its
* parent. You can't traverse a hash table properly if its elements are
- * being deleted. We use only the Tcl_FirstHashEntry function to be safe.
- *
- * Don't optimize to Tcl_NextHashEntry() because of traces.
+ * being deleted. Because of traces (and the desire to avoid the
+ * quadratic problems of just using Tcl_FirstHashEntry over and over, [Bug
+ * f97d4ee020]) we copy to a temporary array and then delete all those
+ * namespaces.
*/
#ifndef BREAK_NAMESPACE_COMPAT
- for (entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search);
- entryPtr != NULL;
- entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search)) {
- childNsPtr = Tcl_GetHashValue(entryPtr);
- Tcl_DeleteNamespace(childNsPtr);
+ while (nsPtr->childTable.numEntries > 0) {
+ int length = nsPtr->childTable.numEntries;
+ Tcl_Namespace **nss = TclStackAlloc((Tcl_Interp *) iPtr,
+ sizeof(Tcl_Namespace *) * length);
+
+ i = 0;
+ for (entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search);
+ entryPtr != NULL;
+ entryPtr = Tcl_NextHashEntry(&search)) {
+ nss[i++] = Tcl_GetHashValue(entryPtr);
+ }
+ for (i = 0 ; i < length ; i++) {
+ Tcl_DeleteNamespace(nss[i]);
+ }
+ TclStackFree((Tcl_Interp *) iPtr, nss);
}
#else
if (nsPtr->childTablePtr != NULL) {
- for (entryPtr = Tcl_FirstHashEntry(nsPtr->childTablePtr, &search);
- entryPtr != NULL;
- entryPtr = Tcl_FirstHashEntry(nsPtr->childTablePtr,&search)) {
- childNsPtr = Tcl_GetHashValue(entryPtr);
- Tcl_DeleteNamespace(childNsPtr);
+ while (nsPtr->childTablePtr->numEntries > 0) {
+ int length = nsPtr->childTablePtr->numEntries;
+ Tcl_Namespace **nss = TclStackAlloc((Tcl_Interp *) iPtr,
+ sizeof(Tcl_Namespace *) * length);
+
+ i = 0;
+ for (entryPtr = Tcl_FirstHashEntry(nsPtr->childTablePtr, &search);
+ entryPtr != NULL;
+ entryPtr = Tcl_NextHashEntry(&search)) {
+ nss[i++] = Tcl_GetHashValue(entryPtr);
+ }
+ for (i = 0 ; i < length ; i++) {
+ Tcl_DeleteNamespace(nss[i]);
+ }
+ TclStackFree((Tcl_Interp *) iPtr, nss);
}
}
#endif