[Kde-bindings] Using threads with QtRuby

Uriel uriel at inbox.ru
Sat Feb 6 08:08:24 UTC 2010


> Yes, this is really good, I think, but it is probably going to be Ruby 
> 1.8.x
> specific. I have no personal fear of monkey patching something as 
> major as the
> Ruby thread api, but it is easy to see why some people could get 
> nervous..
>
> For Ruby 1.9.x instead we will probably need to change the QtRuby code to
> acquire some sort of 'Big Lock' before each method invocation or 
> callback into
> the Ruby runtime like a slot invocation or virtual method callback. So 
> we will
> probably need to make the code above conditional on a Ruby 1.8.x 
> runtime being
> found.
>
> I am not sure it is a good idea to unconditionally override
> Qt::Application.exec() as it involves a performance penalty, which is 
> why I
> suggested having a different Qt::Application.exec_with_threads() (or 
> similarly
> named) method. A large percentage of QtRuby support problems have been 
> to do
> with combining Ruby threads with the Qt event loop, and so it looks to 
> me as
> though your solution would be pretty useful to a lot of people. We 
> just need
> to think through all the issues a bit carefully. As KDE 4.4 is frozen 
> and KDE
> 4.5 isn't for six months I think there is time to sort it out.
I've thought about it for some more time and ended up with an idea of 
how to do it as optionally and conditionally as possible.
Now there is no need to patch anything - just a new class.

class Qt::Thread < ::Thread
         class ThreadsTimer < Qt::Object
             protected
             def timerEvent(ev)
             end

             def initialize
                 super
                 startTimer 0
             end
         end

         @threads_timer = nil

         def Thread.enable_ruby_threads
           # Start timer if only it hasn't been started yet
           return if @threads_timer

           @threads_timer = ThreadsTimer.new
         end
         private_class_method(:enable_ruby_threads)

         def Thread.disable_ruby_threads
           # Stop timer only if we have main thread and just one custom 
which will be stopped right after this call
           return unless Thread.list.count == 2

           @threads_timer.dispose
           @threads_timer = nil
         end
         private_class_method(:disable_ruby_threads)

         def self.decorate_block(*args, &block)
             enable_ruby_threads
             proc{ block.call *args; disable_ruby_threads }
         end
         private_class_method(:decorate_block)

         def self.new(*args, &block)
             super *args, &decorate_block(*args, &block)
         end

         def self.start(*args, &block)
             super *args, &decorate_block(*args, &block)
         end

         def self.fork(*args, &block)
             super *args, &decorate_block(*args, &block)
         end
end

So you can just use Qt::Thread instead of Thread from standard library 
and there is no problems with patching QApplication and Ruby api.
Moreover, as you can see, there is no sleep or pass methods invocation. 
Yes, it's very strange, but it's enough to just start a timer which does 
nothing.
One of the pros of such approach is that we could implement 
started/finished signals of QThread class by simply modifying the block 
passed. It would be very useful at least for me/
But here is the one great con - it's just simply a workaround and 
doesn't eliminate the problem itself.

I have no possibility to test it with Ruby 1.8.x and don't know whether 
it works there or no. But generally I agree that we should change 
QtRuby's code to make threads integration seamless and preform a massive 
testing of such an integration.

And there is two more things I wanted to say on this topic.
First, I were wrong about using widgets from non-gui threads - it still 
fails. You could change the window title, for example, but still can't 
paint or change value of progressbar and so on.
And second is just some kind of bug report.

irb(main):001:0> require 'Qt'
=> true
irb(main):002:0> Qt::Object.new.thread
ArgumentError: Cannot handle 'QThread*' as return-type of QObject::thread
         from (irb):2:in `method_missing'
         from (irb):2
         from /usr/bin/irb:12:in `<main>'

Is is really a bug or it was just kept for the future needs?



More information about the Kde-bindings mailing list