Updated 2012-06-05 20:07:38 by abu

In recent releases, Tcl has a thread-safe core. This is very important in my opinion, since threads are an important component in frontends and GUI applications. Frequently the situation occurs 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 do 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, perhaps coded 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 also not updated synchronously... so the application seems really to be hung.

While threading in Tcl is powerful, it is also very simple: use system threads and create & run a new interpreter in each of them. This way, synchronization mechanisms are seldom necessary, since data is mainly encapsulated in each 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. But one big problem with threads in Tcl is that Itcl objects are not handled. They are just passed as strings to concurrent threads via tsv::set and tsv::get. The access command that every Itcl object has is not transferred, and so the object is not accessible in threads where it was not created. Deep copies, as with simple Tcl data types, are not very efficient and also not as easily possible here.

To get Itcl objects transferred between threads, I worked on the guts of Itcl and created a patch. The changes I made are somewhat massive, so I wrapped up the sources, created binaries for Linux and Windows, as well as built it into my frequently renewed Tclkit. The results can be downloaded here: http://e-lehmann.de/?page_id=22.

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: -threadshared or -ts. It needs to be given only if the object should be accessible in multiple threads. As an example, let's have a class A and create a thread shared object aobj - it would look like:
  A -threadshared aobj ...

or
  A -ts aobj ...

Where the three dots stand for additional options. The object creation mechanism recognizes the flag and creates & stores 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, if Itcl is loaded. When the object is deleted while there are access commands in different threads, just its 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 implemented is particularly important: there is no interference 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. This applies also if the object was created with -threadshared and there is only one thread. 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 (additionally the tests I added pass as well).

The workflow for an Itcl developer who wants 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 ...
  • use tsv::set/tsv::get to set/get the object's name as shared variable if you don't know it in advance (e.g. if ::auto was used)
  • 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. Classes are not automatically transferred, they need to be loaded (package re ... or source) explicitly in the worker thread.

Simultanous access works via shared ItclObject* and ItclClass* structures. That's why it is necessary to have synchronization in place. To make this easy, the built in configure and cget methods are synchronized by default via recursive mutexes, except the config/cget code sections. For everything else, I implemented a new command, itcl::synchronize, that evaluates its body in a thread-safe environment via recursive mutex locking. A usage 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 and the body is only run in one thread at one time - as you will want to have it. It is usually necessary when the object's variables need to be modified or if they otherwise shouldn't be accessed asyncronously. Without synchronize at proper places, strange things can happen - from memory access errors to inconsistent results.

[mikespry] - 2012-06-05 19:37:53

anyone know where the source or binaries can by downloaded for this itcl patch? i'm looking for a way to share objects between tcl threads and this seemed like a good thing to try...if only the download link worked.

ABU 6 jun 2012

I wonder if this mechanism could provide a simple method for sharing images between threads. Since loading and decoding a large image may takes several seconds, I ve tried to load an image in a worker thread, but then when the image data should be transferred to the main thread, it takes even more time ! If images could be shared between threads, it would be a great improvement. Do you think is it possible, or it s better to explore alternatives ?