summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/ttk/ttkTrace.c29
1 files changed, 29 insertions, 0 deletions
diff --git a/generic/ttk/ttkTrace.c b/generic/ttk/ttkTrace.c
index a469d0b..f171f3d 100644
--- a/generic/ttk/ttkTrace.c
+++ b/generic/ttk/ttkTrace.c
@@ -44,6 +44,11 @@ VarTraceProc(
* If the variable is being unset, then re-establish the trace:
*/
if (flags & TCL_TRACE_DESTROYED) {
+ /*
+ * If a prior call to Ttk_UntraceVariable() left behind an
+ * indicator that we wanted this handler to be deleted (see below),
+ * cleanup the ClientData bits and exit.
+ */
if (tracePtr->interp == NULL) {
Tcl_DecrRefCount(tracePtr->varnameObj);
ckfree((ClientData)tracePtr);
@@ -111,12 +116,36 @@ void Ttk_UntraceVariable(Ttk_TraceHandle *h)
if (h) {
ClientData cd = NULL;
+ /*
+ * Workaround for Tcl Bug 3062331. The trace design problem is
+ * that when variable unset traces fire, Tcl documents that the
+ * traced variable has already been unset. It's already gone.
+ * So from within an unset trace, if you try to call
+ * Tcl_UntraceVar() on that variable, it will do nothing, because
+ * the variable by that name can no longer be found. It's gone.
+ * This means callers of Tcl_UntraceVar() that might be running
+ * in response to an unset trace have to handle the possibility
+ * that their Tcl_UntraceVar() call will do nothing. In this case,
+ * we have to support the possibility that Tcl_UntraceVar() will
+ * leave the trace in place, so we need to leave the ClientData
+ * untouched so when that trace does fire it will not crash.
+ */
+
+ /*
+ * Search the traces on the variable to see if the one we are tasked
+ * with removing is present.
+ */
while ((cd = Tcl_VarTraceInfo(h->interp, Tcl_GetString(h->varnameObj),
0, VarTraceProc, cd)) != NULL) {
if (cd == (ClientData) h) {
break;
}
}
+ /*
+ * If the trace we wish to delete is not visible, Tcl_UntraceVar
+ * will do nothing, so don't try to call it. Instead set an
+ * indicator in the Ttk_TraceHandle that we need to cleanup later.
+ */
if (cd == NULL) {
h->interp = NULL;
return;