Movatterモバイル変換
[0]ホーム
[Python-Dev] Tcl, Tkinter, and threads
Martin v. Löwismartin@v.loewis.de
12 Dec 2002 19:18:37 +0100
I have now applied changes to _tkinter to make it work when Tcl wasbuilt with --enable-threads. For those of you interested in such kindof stuff, here is the full story.This work was triggered by the bug reporthttp://bugs.debian.org/171353where pydoc -g crashes Tcl. It turns out that pydoc uses threads, andinvokes a button configure command from a thread different from theone where the Tcl interpreter was called. Tcl uses thread-localstorage to associate a TkDisplay* with a X Display*, and that TLS wasuninitialized in this other thread.Discussion with Tcl people athttp://sourceforge.net/tracker/index.php?func=detail&aid=649209&group_id=12997&atid=112997lead to the conclusion that this is intentional; they call it the"appartment model"; you can use the Tcl interpreter only from thethread that has created it, you cannot pass Tcl_Obj* across threadboundaries, and you cannot invoke Tcl commands from other threads.If Tcl is not built for threads, this all is not relevant, since thereis only a single set of "thread-local" data (i.e. it isn't thread-local);the thread API is still there, anyway.To overcome this limitation, I now use Tcl Queues to pass commandsfrom one thread the other; this is also how the Tcl thread extensionworks. You allocate a Tcl_Event, fill some data, put it into a queue,alert the target thread, and block on a Tcl_Condition. The targetthread fetches the event from the queue, invokes the callbackfunction, which performs the Tcl action, and notifies the condition.Passing of results happens to stack variables in the calling threadwhose addresses are put into the event.This kind of marshalling now happens for the following APIs:- call: passes the PyObject* args to the target thread. There it is converted into Tcl objects, the command is invoked, and the result is converted back to a Python object.- getvar/setvar/unsetvar: pass the variable name and value, and invoke the Tcl API in the target thread.- createcommand/deletecommand: likewise.For a few APIs, this marshalling is not possible in principle:- mainloop/doonevent: event processing must happen in the interpreter thread, by nature of the Tcl threading modelFor a larger number of APIs, I found the effort not worthwhile:- globalcall, eval, globaleval, evalfile, record, adderrorinfo, exprstring, exprlong, exprdouble, exprboolean, createfilehandler, deletefilehandlerFor all these functions, _tkinter will now raise an exception if theyare invoked in the wrong thread.A tricky question is what happens if the target thread is notprocessing events right now, either because it mainloop hasn't beeninvoked, or because it is busy doing something else. The current coderaises an exception if the target interpreter is not in the mainloop,and blocks (potentially indefinitely) if the target process does notunqueue its events.This might cause problems if you create multiple interpreters in onethread: it would be sufficient if one of them processesevents. Currently, calls to the other interpreters will raise theexception that the mainloop has not been entered. I hope this won'tcause problems in practice, since you rarely have more than oneinterpreter.With these changes, it would now be possible to build Tcl in threadedmode on Windows. This has both advantages and disadvantages:+ It allows to get rid of the Tcl lock, and the nearly-busy wait.- It may cause problems for existing applications if they run into one of the limitations. Of course, those applications will run into the same limitations on Unix if Tcl was build with threads enabled.If Tcl wasn't build with threads enabled, behaviour is nearlyunmodified. Python will still invoke the Tcl thread API, but thatwon't do anything, so there should be no user-visible change.Regards,Martin
[8]ページ先頭