[Kde-bindings] Using threads with QtRuby

Richard Dale rdale at foton.es
Mon Feb 8 20:11:13 UTC 2010

On Saturday 06 February 2010 08:08:24 am Uriel wrote:
> > 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. 
Is that correct or you mean you haven't tested with the new Ruby 1.9.x 

> 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.
Yes, QObjects are associated with a certain thread, and I think only the main 
thread can do rendering otherwise it would be hard to coordinate.

> 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?
No, it isn't a bug as the QThread class isn't in the Smoke library as each 
bindings language needs to implement threads integrated with their own 
threading system. But I think calling the QtRuby threads Qt::Thread like 
you've done about is a good idea as it make it clear that they are nearly like 
Ruby threads - just a subclass with customization.

We would need to special case methods like Qt::Object.thread so that they 
returned a Ruby thread. But that would only make sense if the Ruby 
implementation was using native threads that mapped onto QThreads 1:1. It 
wouldn't make sense for Ruby 1.8.x threads based on setjmps/longjmps.

-- Richard

More information about the Kde-bindings mailing list