Tkinter crash (panic) in call to Tcl_AppendFormatToObj

784 views Asked by At

I wrote a Python application that uses Tkinter for a GUI. It has a second thread for TCP/IP communication (XMLRPC actually, allowing labview to connect and make some calls). The two threads communicate with a pair of thread safe queues. After running for a couple of days, it the crashes. I don't get a full trace back. So far the best I got was "Tcl_AppendFormatToObj called with shared object" Evidently, this comes from the following Tcl function:

Tcl_AppendFormatToObj(..)
{
...
    if (Tcl_IsShared(appendObj)) {
    Tcl_Panic("%s called with shared object", "Tcl_AppendFormatToObj");
    }

Something about formatting a string if causing a problem. Any advice? I'm running the script on Windows 7 under the winpdb now and waiting and hoping for a useful trace when the "panic" happens again.

2

There are 2 answers

0
user3145004 On BEST ANSWER

It turns out that one of the queues was not thread safe. The Tk even queue is not thread safe. So using tk's root.after() to put something on tk's even queue, from another thread, cause intermittent problems. Instead, create a real queue, and make the non tk loop thread stuff something into the queue, and make code in the tk thread monitor the other side of the queue. So in the tk loop, write a function that empties the real queue ( and do something based on what it found). Make that function call itself with after().

1
Donal Fellows On

The semantics of Tcl's Tcl_Obj value system — which does not have value identity, unlike in Python — requires values to be observably constant. In practice, this means that where there is a single reference (e.g., from a Tcl variable) the value may be modified, but where there are multiple references (two variables, a variable and an argument, etc.) the value may not be changed (other than to have its type mutated, a process known in Tcl parlance as “shimmering”, because types are not meant to be observable facets of values in Tcl) in order to prevent “spooky at-at-distance” mutation. These rules are enforced by the value mutation functions, of which Tcl_AppendFormatToObj is one.

Why there are multiple references to a value that is being mutated is somewhat less clear. I'm guessing based on what you say that you've got a race between a thread that is mutating the value and another thread that is using the value (during which time it temporarily acquires a second reference). Race conditions are ugly to hunt down in all languages. At the low-level, the fix would usually be to use Tcl_DuplicateObj to take an unshared copy in the mutating thread that can then be written to the shared storage after the change is made, but I suspect that this will break things elsewhere in your threaded code where you may be assuming on the reader side that the reference doesn't change.

Sorting that out is going to be tricky. The Tcl way to fix this is to stop trying to share values across threads and to instead switch to message passing, where the “writer” thread actually sends a message to the “reader” thread to update its state. (This does scale up to having multiple readers if necessary, so it isn't all bad, and can be done without global locking of state, which is even better. Tcl doesn't need anything like Python's global interpreter lock, because it partitions everything much more strongly by thread in the first place.) I don't know how to translate this into things that you can implement in Python…