From 07169bb2f60d244e9083a77c24fff0f964502568 Mon Sep 17 00:00:00 2001 From: dgp Date: Thu, 16 Jun 2011 20:27:22 +0000 Subject: New attempt to fix Bug 3062331 --- generic/ttk/ttkTrace.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/generic/ttk/ttkTrace.c b/generic/ttk/ttkTrace.c index 0128a1d..a469d0b 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 (tracePtr->interp == NULL) { + Tcl_DecrRefCount(tracePtr->varnameObj); + ckfree((ClientData)tracePtr); + return NULL; + } Tcl_TraceVar(interp, name, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, VarTraceProc, clientData); @@ -104,6 +109,18 @@ Ttk_TraceHandle *Ttk_TraceVariable( void Ttk_UntraceVariable(Ttk_TraceHandle *h) { if (h) { + ClientData cd = NULL; + + while ((cd = Tcl_VarTraceInfo(h->interp, Tcl_GetString(h->varnameObj), + 0, VarTraceProc, cd)) != NULL) { + if (cd == (ClientData) h) { + break; + } + } + if (cd == NULL) { + h->interp = NULL; + return; + } Tcl_UntraceVar(h->interp, Tcl_GetString(h->varnameObj), TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, VarTraceProc, (ClientData)h); -- cgit v0.12 From d96f0a1840e687316f7fb0d8153862a1ae684240 Mon Sep 17 00:00:00 2001 From: dgp Date: Fri, 17 Jun 2011 18:06:52 +0000 Subject: Add comments explaining new code. --- generic/ttk/ttkTrace.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) 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; -- cgit v0.12