BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Ruby Userspace Threads vs GUI toolkits Roundup

Ruby Userspace Threads vs GUI toolkits Roundup

Blogger Ciaran McCreesh seems concerned with the lack of kernel threads in Ruby and ponders consequences for GUI applications:

A simple example: Consider a Gtk2 GUI application with a button and a text box. When the user presses the button, the application goes away and does some calculations using a third party library. When it is done, it displays the results in the text box. To prevent confusion, the button should be greyed out whilst the calculations are in progress. The naive implementation has a problem: whilst the calculations are taking place, the GUI can't respond to events. If, for example, the user switches away from and back to the window, the window won't get redrawn and it will look like the application has hung. The usual solution is to use threading. There are various ways of doing this and handling the communication, some more elegant than others. In any case, a child thread ends up doing the calculation whilst the parent thread remains free to handle GUI events. Except that with Ruby, this isn't an option. Using operating system threads (e.g. pthreads or Gdk threads) makes the Ruby interpreter explode. And when using Ruby threads, the thread handling the GUI won't get called so long as the child thread is busy in third party code.

 

While the lack of kernel threads (threads scheduled and handled by the OS) can be a problem, it is not an unsolvable. I/O is the big issue, particularly network access, since it can potentially block for long times. If this happens on the GUI event handling thread, it will leave the GUI unresponsive for the duration. That problem is solvable by using non-blocking I/O with the select call. select allows to specify timeouts for I/O operations, such as waiting for input from a socket. If no data has arrived before the timeout, the call returns and the code is free to yield to other threads, for instance the thread handling GUI events. Later on the I/O operation can be tried again. By using short timeouts, this acts like time slicing mechanisms that thread schedulers use.

As a matter of fact, many Smalltalk implementations like Squeak or Cincom VisualWorks also use user-space threads, and their proponents see it as an advantage. Cincom's Product Manager James Robertson expresses his surprise at the plans for Ruby 2.0 to have kernel threads (Note: he refers to user-space threads as green threads, to kernel threads as native threads):

In VisualWorks, threads are green. This makes them really, really inexpensive. For instance, in BottomFeeder, I spawn a thread per feed (I'm subscribing to 309 as I write this). I don't need to worry about thread pools, or overwhelming the platform. If I tried such a feat with native threads, I'd have to worry about those things.
There are a few remaining problems, as pointed out in the comments. One example is a system call like DNS lookup, which cannot be worked around with non-blocking I/O methods. James Robertson in the comments of the above article:
At present, DNS lookups do, in fact, block at the VM level. We are implementing an asynch client for in order to deal with that OS level limitation.
As an aside, Rubinius has added first support for user-space threads. Rubinius is a Ruby runtime implementation that draws heavily on the design behind Squeak and the "Blue Book", a description of the Smalltalk-80 system.

 

Other options are to use the OS scheduler to handle this by starting multiple Ruby processes, which has the benefit of exploiting multiple CPUs or Cores if they are available (the OS can schedule the runtimes to available CPUs or Cores). The runtimes can interact via appropriate methods of Inter-Process Communication (IPC). Ruby ships with libraries such as DRb that make communication easy. Using sockets makes it possible to use non-blocking I/O for communication with other runtimes. By offloading long running tasks to other runtimes, the GUI can be kept responsive. This also helps to enforce decoupling the GUI from the backend of an application, by making every communication between the two explicit and shortcuts impossible. Being single threaded and thus removing the troubles of pre-emptive multithreading - such as synchronization or race conditions - is an idea implemented by languages such as Erlang.

An alternative solution presents itself in JRuby, a Ruby implementation for the Java platform. JVMs have been using kernel threads for many years (the first versions of Java used green threads too). JRuby's solution to handling threads is to map each Ruby thread onto a Java thread, thus reaping the advantages of kernel threads. Since many toolkits have Java bindings, using them from JRuby is an option. JavaGnome allows access to Gnome and GTK+, Qt Jambi(tm) gives access to Trolltech's popular Qt GUI toolkit. Of course, other choices such as SWT, the GUI toolkit behind Eclipse and BitTorrent client Azureus, are possible as well.

Rate this Article

Adoption
Style

BT