summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordgp <dgp@users.sourceforge.net>2016-09-07 18:09:30 (GMT)
committerdgp <dgp@users.sourceforge.net>2016-09-07 18:09:30 (GMT)
commit48203a2fef2dfbef11cc1ba5d5aaf778f9603a67 (patch)
treeee41ace108164ac84cf9846a9512b9b7290abcd3
parent126933978f910cb47d9bf73c99e1c9eacb57ed04 (diff)
parentb2285feb18ebbcf95efeec554d49294c0d6c499a (diff)
downloadtcl-48203a2fef2dfbef11cc1ba5d5aaf778f9603a67.zip
tcl-48203a2fef2dfbef11cc1ba5d5aaf778f9603a67.tar.gz
tcl-48203a2fef2dfbef11cc1ba5d5aaf778f9603a67.tar.bz2
[4dbdd9af14] Plug mem leak when var unset trace re-creates namespace var. Thanks mr_calvin for report and fix.
-rw-r--r--generic/tclVar.c24
-rw-r--r--tests/var.test16
2 files changed, 35 insertions, 5 deletions
diff --git a/generic/tclVar.c b/generic/tclVar.c
index 20bc208..48e09f6 100644
--- a/generic/tclVar.c
+++ b/generic/tclVar.c
@@ -4956,13 +4956,16 @@ TclDeleteNamespaceVars(
VarHashRefCount(varPtr)++; /* Make sure we get to remove from
* hash. */
Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, objPtr);
- UnsetVarStruct(varPtr, NULL, iPtr, /* part1 */ objPtr, NULL, flags,
- -1);
- Tcl_DecrRefCount(objPtr); /* Free no longer needed obj */
+ UnsetVarStruct(varPtr, NULL, iPtr, /* part1 */ objPtr,
+ NULL, flags, -1);
/*
- * Remove the variable from the table and force it undefined in case
- * an unset trace brought it back from the dead.
+ * We just unset the variable. However, an unset trace might
+ * have re-set it, or might have re-established traces on it.
+ * This namespace and its vartable are going away unconditionally,
+ * so we cannot let such things linger. That would be a leak.
+ *
+ * First we destroy all traces. ...
*/
if (TclIsVarTraced(varPtr)) {
@@ -4986,6 +4989,17 @@ TclDeleteNamespaceVars(
}
}
}
+
+ /*
+ * ...and then, if the variable still holds a value, we unset it
+ * again. This time with no traces left, we're sure it goes away.
+ */
+
+ if (!TclIsVarUndefined(varPtr)) {
+ UnsetVarStruct(varPtr, NULL, iPtr, /* part1 */ objPtr,
+ NULL, flags, -1);
+ }
+ Tcl_DecrRefCount(objPtr); /* free no longer needed obj */
VarHashRefCount(varPtr)--;
VarHashDeleteEntry(varPtr);
}
diff --git a/tests/var.test b/tests/var.test
index 44e671a..9816d98 100644
--- a/tests/var.test
+++ b/tests/var.test
@@ -594,6 +594,22 @@ test var-8.2 {TclDeleteNamespaceVars, "unset" traces on ns delete are called wit
list [namespace delete test_ns_var] $::info
} -result {{} {::test_ns_var::v {} u}}
+test var-8.3 {TclDeleteNamespaceVars, mem leak} -constraints memory -setup {
+ proc ::t {a i o} {
+ set $a 321
+ }
+} -body {
+ leaktest {
+ namespace eval n {
+ variable v 123
+ trace variable v u ::t
+ }
+ namespace delete n
+ }
+} -cleanup {
+ rename ::t {}
+} -result 0
+
test var-9.1 {behaviour of TclGet/SetVar simple get/set} -setup {
catch {unset u}
catch {unset v}