In recent releases, Tcl has got a thread save core. This is very important in my opinion, since Threads are an important component in frontends and GUI applications. Frequently the situation occures that you have a long running piece of code that would block the whole application if running in the same thread. This is annoying for the user - she just sees that the application does not do anything anymore and does not know what has happened: “Did I something wrong? Has it crashed? When can I continue to work? Where is the support hotline…?” The long running piece of code can be a number-crunching procedure, coded maybe in C and interfaced with the frontend code. It also can be a network connection to fetch updates of application data or anything else. The latter could be solved with Tcl’s event loop without the need for threads, but the former can not. The main problem in this context is, that you can not even display a progress bar or dialog during the time when the GUI is blocked, because the display is as well not updated syncronously… so the application seems really to be crashed. At least since 8.4, the core of Tcl is thred save. While it is powerful, this threading model is also very simple: use system threads and create & run a new slave interpreter in each of them. This way it is almost never necessary to work with syncronization mechanisms, every piece of data is encapsulated in every thread. It is possible to share variables via the tsv::* commands from the threads extension that can be downloaded from http://tcl.sourceforge.net/. These commands work by making deep copies of variables and values between the thread interpreters - which creates a slight overhead on the cost of safety. One big problem with the official thread extension is, that it is not able to handle Itcl objects. They are just passed as strings to concurrent threads via tsv::set and tsv::get. The access command is not transfered and therefore the object is not accessible. Deep copies, as with simple Tcl data types, are not very efficient and also not as easily possible for Itcl. To get it done, I worked on the guts of Itcl and created a patch against the latest CVS version. The changes are somewhat massive, so I also wrapped up the sources, created binaries for Linux and Windows, and also built it into my frequently renewed Tclkit. The results can be downloaded here: http://e-lehmann.de/?page_id=31. ---- '''How it works''' There is nothing particular new to the usage of Itcl itself. It works like before, the only bit that changed is an additional flag during object creation. It's the ''-threadshared'' or ''-ts'' flag that needs to be given only, if a newly created object should be accessible in multiple threads. Let's have a class ''A'' and create a thread shared object ''aobj'', it would look like this: A -threadshared aobj ... or A -ts aobj ... Where the three dots stand for additional options. The object creation mechanism recognizes the flag and creates an access command for the new object in every present thread. The flag is also stored as object specific data and an access command is created for every new thread that is created and loaded with Itcl. When the object is deleted while there are access commands in different threads, it's thread specific access command is just deleted. So it is not available anymore in a particular thread after deletion in that thread. When the object is deleted while it has an access command just in one thread, then the normal deletion process takes place (decrementing the refcount and eventually object deletion). The way it is done is particular important. It means that there is '''no inference''' with old Itcl code that runs in single-threaded environments. If the object is created without ''-threadshared'', it behaves just like it does in one thread. Also, if there is only one thread and the object was created with ''-threadshared'', it behaves the same way as before. So, nothing changes and old Itcl programs can be safely run with the new, threaded Itcl. I have verified this by running the test suite over and over. All 401 tests from the official Itcl pass and additionally the tests I added pass as well. The workflow for an Itcl developer who want's to take advantage of the threaded Itcl is relatively simple: * create the class(es) in one thread and create the objects with the ''-ts'' option: ''ClassName -ts objname ...'' This makes sure that the object is given an access command in every running and new thread * use ''tsv::set''/''tsv::get'' to set/get the object's name as ''shared variable''. This is not necessary if you know the objects name in advance, of course, but helpful if you work with ''::auto'' * modify the object in other threads, call methods on them, set values, whatever you want * the objects are updated simultaneously in all threads. It is possible to create Itcl objects in whatever thread and transfer their names through ''tsv::set'' and ''tsv::get''. Classes are not transferred. If you want to be able to instantiate new objects in different threads, you have to make sure that the classes are loaded (''package re'' ... ''or source'') in the worker thread. Simultanous access works via shared ItclObject* and ItclClass* structures. That's why it is necessary to have syncronization in place - Itcl developers have to be aware of this when it comes to multi-threaded applications. To make it easy, the builtin ''configure'' and ''cget'' methods are syncronized by default via recursive mutexes, except the config/cget code sections. For everything else, I implemented a new command ''itcl::synchronize'', that evaluates it's body in a thread save environment. An example can be seen in tests ''thread-1.7'' and ''thread-1.8'' in tests/thread.test. The command usage is: ''itcl::synchronize body'' The command can only be used inside an object context, that means inside object methods. The ''body'' is only run from one thread at one time. Usually you will want to have it that way if you like to modify the object or if you don't want to have it modified during an access. Be careful to use ''synchronize'' whenever it is needed, otherwise strange things can happen - from memory access errors to inconsistent results.