Itcl & threads

Tcl is one of the rare dynamic languages that are really thread save and make use of system threads. 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.

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.

A big problem is, that Itcl objects are not accessible in different threads. They are just passed as strings via tsv::set and tsv::get. The access command is not transfered and therefore the object is not accessible. For Tloona (look here) there is a concrete use case for accessing objects in different threads: I want to be able to run time consuming tasks in multiple threads so that the application does not always block. One of these tasks is the reparsing of the syntax tree in Tcl files, after the user has saved it. If the file gets bigger (let’s say up to 10000 lines), reparsing takes some time. Parsing is deeply involved with the Itcl based parser that I wrote and can not be separated. The only way to run it in a different worker thread is, to transfer the complete tree object, run the reparsing asyncronously and then update the tree view in the main thread on completion.

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 there is no patch. Instead there are the modified sources as part of Tclkick, which itself is part of Tloona at sourceforge.


How it worksThere 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. Due to the flag, an access command is also created for every new thread on package re Itcl. When the object is deleted while there are access commands in different threads, just it’s thread specific access command is 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 syncronize whenever it is needed, otherwise strange things can happen – from memory access errors to inconsistent results. Well, that is not new to you – once you come to multi-threaded programming you will need to syncronize your data anyway.. ;-)

Download: Check out the sources from the tclkick svn repository and compile it by yourself. As for binaries – there is always a recent binary of Itk as part of the Tloona release and Itcl is built into the tclkick.

Comments Off