[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
threads?
> 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