[Python-Dev] Making Tcl/Tk more suitable for embedding (was: [issue33479] Document tkinter and threads)

Ivan Pozdeev ivan_pozdeev at mail.ru
Tue May 15 13:43:45 EDT 2018

Subj is off topic for the ticket, so I guess this discussion is better 
continued here.

On 15.05.2018 18:20, Mark Roseman wrote:
> Mark Roseman <mark at markroseman.com> added the comment:
> Hi Ivan, thanks for your detailed response. The approach you're suggesting ("Since the sole offender is their threading model, the way is to show them how it's defective and work towards improving it.") is in the end not something I think is workable.
> Some historical context. Ousterhout had some specific ideas about how Tcl/Tk should be used, and that was well-reflected in his early control of the code base. He was certainly outspoken against threads. The main argument is that they're complicated if you don't know what you're doing, which included the "non-professional programmers" he considered the core audience. Enumerating how threads were used at the time, most of the uses could be handled (more simply) in other ways, such as event-driven and non-blocking timers and I/O (so what people today would refer to as the "node.js event model"). Threads (or separate communicating processes) were for long-running computations, things he always envisioned happening in C code (written by more "professional programmers"), not Tcl. His idea of how Tcl and C development would be split didn't match reality given faster machines, more memory, etc.

Very enlightening. Many thanks.

> The second thing is that Tcl had multiple interpreters baked in pretty much from the beginning at the C level and exposed fairly early on (1996?) at the Tcl level, akin to PEP 554. Code isolation and resource management were the key motivators, but of course others followed. Creating and using Tcl interpreters was quick, lightweight (fast startup, low memory overhead, etc.) and easy. So in other words, the notion of multiple interpreters in Tcl vs. Python is completely different. I had one large application I built around that time that often ended up with hundreds of interpreters running.

Not familiar with the concept so can't say atm if tkinter can make any 
use of this. All tkinter-using code I've seen so far only ever uses a 
single tkinter.Tk() -- thus a single interpreter.

> Which brings me to threads and how they were added to the language. Your guess ("My guess for the decision is it was the easiest way to migrate the code base") is incorrect. The idea of "one thread/one interpreter" was just not seen as a restriction, and was a very natural extension of what had come before. It fit the use cases well (AOLserver was another good example) and was still very understandable from the user level. Contrast with Python's GIL, etc.

I'm not actually suggesting any changes to Tcl as a language, only to 
its C interface (details follow).
AFAIK Tcl also advertises itself as an embeddable language as its main 
selling point, having been developed primarity as an interface to Tk 
rather than a self-sufficient language (or, equivalently, this being its 
primary use case now). Having to do an elaborate setup with lots of 
custom logic to be able to embed it is a major roadblock. This can be 
the leverage.

 From C interface's standpoint, an interpreter is effectively a bunch of 
data that can be passed to APIs. Currently, all Tcl_* calls with a 
specific interpreter instance must be made from the same thread, and 
this fact enforces sequential access. I'm suggesting to wrap all these 
public APIs with an interpreter-specific lock -- so calls can be made 
from any OS thread and the lock enforces sequential access. For Tcl's 
execution model and existing code, nothing will change.
The downside (that will definitely be brought up) is the overhead, of 
course. The question is thus whether the above-mentioned benefit 
outweighs it.

> With that all said, there would be very little motivation to change the Tcl/Tk side to allow multiple threads to access one interpreter, because in terms of the API and programming model that Tcl/Tk advertises, it's simply not a problem. Keep in mind, the people working on the Tcl/Tk core are very smart programmers, know threads very well, etc., so it's not an issue of "they should know better" or "it's old." In other words, "show them how it's defective" is a non-starter.
> The other, more practical matter in pushing for changes in the Tcl/Tk core, is that there are a fairly small number of people working on it, very part-time. Almost all of them are most interested in the Tcl side, not Tk. Changes made in Tk most often amount to bug fixes because someone's running into something in their own work. Expecting large-scale changes to happen to Tk without some way to get dedicated new resources put into it is not realistic.
> A final matter on the practical side. As you've carefully noted, certain Tcl/Tk calls now happen to work when called from different threads. Consider those a side-effect of present implementation, not a guarantee. Future core changes could change what can be called from different threads, making the situation better or worse. From the Tcl/Tk perspective, this is not a problem, and would not be caught by any testing, etc. Even if it were, it likely wouldn't be fixed. It would be considered an "abuse" of their API (I think correctly).
> My suggestion, given the philosophical and practical mismatch, is that Tkinter move towards operating as if the API Tk provides is inviolate. In other words, all calls into a Tcl interpreter happen from the same thread that created the Tcl interpreter. Tkinter acts as a bridge between Python and Tcl/Tk. It should present an API to Python programs compatible with the Python threading model. It's Tkinter's responsibility to map that onto Tcl/Tk's single threaded API through whatever internal mechanism is necessary (i.e. pass everything to main thread, block caller thread until get response, etc.)

That's exactly what Tkinter currently does, see the letter attached to 
the ticket. Writing clean and correct code is Python's principal 
standpoint, it doesn't use unsupported functions.

> I'd go so far as to suggest that all the Tkapp 'call' code (i.e. every place that Tkinter calls Tcl_Eval) check what thread it's in, and issue a warning or error (at least for testing purposes) if it's being called from the "wrong" thread. Having this available in the near future would help people who are debugging what are fairly inexplicable problems now.
> The approach of making Tkinter responsible also has the advantage of dealing with far more Tcl/Tk versions and builds.
> Given in practice that few people are really running into things, and that if they are, they know enough to be able to follow the instruction "all Tkinter calls from the same thread" for now, add the warnings/errors in via whatever "turn on debugging" mechanism makes sense. A future version of Python would include a fully thread-safe Tkinter that internally makes all Tcl/Tk calls from a single thread, as per above.
> Sorry this is so incredibly long-winded. I hope the context at least is useful information.
> ----------
> _______________________________________
> Python tracker <report at bugs.python.org>
> <https://bugs.python.org/issue33479>
> _______________________________________


More information about the Python-Dev mailing list