Forum: Ruby-Gnome 2 gtk and threaded scripts - compatible?

Posted by Roberto Cm (roberto_c)
on 2010-05-16 14:11
I've got a script that launches one thread to show a gui progress bar
while another thread sends some files. The gui thread will hang, in a
bad way, with:

/home/roberto/Código/enviado_de_web/lib/gui.rb: line 42
   Gtk-CRITICAL **:gtk_main_quit: assertion `main_loops != NULL' failed

I say "in a bad way" because the thread for the gui does not die on
Ctl-C -- not even if I specifically trap the SIGINT and do the
Gtk.main_quit from there. -- it zombies on my system and I have to hunt
down the thread and kill -9 it:

[506] roberto@quad-g5: ~/Código/enviado_de_web [8:03am]$ ps aux|grep
ruby
roberto  17949  7.4  0.9 139852 39388 pts/0    Sl   08:03   0:01 ruby
./plugin foobar ?
roberto  17977  0.0  0.0   3608   936 pts/0    S+   08:03   0:00 grep
ruby
[507] roberto@quad-g5: ~/Código/enviado_de_web [8:03am]$ kill -9 17949

my project is only a couple hundred lines of code, available on git:
http://github.com/robbiemu/Enviado-de-web

obviously still in an unusable state :)
Posted by Mike Charlton (Guest)
on 2010-05-16 15:40
(Received via mailing list)
Hmmm... I'm a little bit confused by your code.  I don't see a
Gtk.init or Gtk.main anywhere.  I probably just missed it.
Your threads look a little confusing to me too.  You've got
a thread sitting there doing Gtk.main_iteration forever
with no way to exit.

 I admit that I avoid threads altogether unless I have no
choice.  But I think I've beaten that dead horse enough ;-)
Here are some thoughts:

I think putting your GTK code in a new thread itself is
probably a bad idea.  I suspect it should work, but the
threading code in ruby is different than the threads used
by GTK.  Sometimes there are problems.  If you want to
use threads with GTK you should stick to simple
implementations.

I also seem to remember that GTK calls *must* be in
the same thread that GTK is started from.  This means
that you have to be a bit careful anyway.  A much, much
simpler way to do what you want is to run your network
code in a separate thread and have a Gtk.on_idle
function update your progress bar.

But honestly, even that is complicated, since you probably
want to control your app from the gui.  Properly syncronizing
your threads so that you don't accidently call GTK code from
a non-GTK thread is a pain in the butt.  A much, much, much
simpler approach is to avoid using threads altogether.

If you are sending long files, write a method that simply sends
a few hundred bytes (or whatever you want).  When you
want to send your file, add the method to Gtk.on_idle.
When the file is finished, remove it from on_idle.  Then you
can update the gtk status directly from your file sending method
if you like.  Especially if you use non-blocking writes, your
UI will be insanely responsive at all times too.

Wait... I promised not to beat the dead horse... ;-)

           MikeC
Posted by Roberto Cm (roberto_c)
on 2010-05-16 16:13
Mike Charlton wrote:
> Hmmm... I'm a little bit confused by your code.  I don't see a
> Gtk.init or Gtk.main anywhere.  I probably just missed it.
> Your threads look a little confusing to me too.  You've got
> a thread sitting there doing Gtk.main_iteration forever
> with no way to exit.

(reading the rest of your message still)

Can I call Gtk.init? I thought that all happened behind the scenes.
Gtk.main_iteration is supposed to be the same as 1 pass in Gtk.main .. 
so wrapping it in an appropriate while loop is equivalent to running 
Gtk.main -- but this way I get to send updates to my progress bar while 
they happen, instead of all at once at the beginning/end. -- or that's 
not right?
Posted by Roberto Cm (roberto_c)
on 2010-05-16 16:43
Mike Charlton wrote:
>  I admit that I avoid threads altogether unless I have no
> choice.

with the average processor today presenting an apparent 8 cores, that's 
no longer a sustainable way to code, I'm afraid. :)

> I think putting your GTK code in a new thread itself is
> probably a bad idea.  I suspect it should work, but the
> threading code in ruby is different than the threads used
> by GTK.  Sometimes there are problems.  If you want to
> use threads with GTK you should stick to simple
> implementations.

This is about as simple as it gets man :) -- did you read the gui code?

> I also seem to remember that GTK calls *must* be in
> the same thread that GTK is started from.

that is interesting. is that "started" the Gtk.init that comes with 
require "gtk2" ? Or is it the Gtk.main -- they're actually different 
threads here.

Why isn't that in the ruby-gnome2 api docs? I hate it when a 
specification creates several equivalent ways of doing something, only 
one of which actually work. :P

This whole section in my code, will be refactored out to the gui.rb lib 
and run entirely in that thread later, but that's once I build a 
readline version of the progress bar and need to offer the user the 
option of using that. The idea of using a thread for this came from the 
fact that I don't know how the plugins will work. so its best to let 
them update based on the ruby thread scheduler rather than forcing the 
plugin programmers to impliment their own schedule. especially when the 
gui will be abstracted out as other guis are added in optionally later.

  This means
> that you have to be a bit careful anyway.  A much, much
> simpler way to do what you want is to run your network
> code in a separate thread and have a Gtk.on_idle
> function update your progress bar.

Gtk.on_idle is missing from the ruby gnome 2 api documentation :S lol, 
can you up an example please?

> But honestly, even that is complicated, since you probably
> want to control your app from the gui. 

I don't. I literally only need a progress bar. I dont even need a main 
window.

> If you are sending long files, write a method that simply sends
> a few hundred bytes (or whatever you want).

It would be fast but then I would be responsible for all the plugins and 
the system wouldnt be externally extensible.

> Wait... I promised not to beat the dead horse... ;-)
> 
>            MikeC

thanks Mike
Posted by Mike Charlton (Guest)
on 2010-05-16 18:09
(Received via mailing list)
On 16 May 2010 23:43, Roberto Cm <ruby-forum-incoming@andreas-s.net> 
wrote:
> one of which actually work. :P
I'm not the author of GTK or ruby-gtk.  I'm just trying to help you
with your problem.  Actually, looking at the documentation it seems
that you are correct in your assumption that init() does not need to
be called.  it gets called when you include gtk2.rb

http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk#Gtk.init

My guess is that you need to do your calls to gtk in the
same thread that Gtk.init happens in (i.e. where you included
gtk2.rb)  This may be one of the sources of your problems.
The problem as I remember is that GTK has some threads
running in it and they are incompatible with ruby threads.

There has been discussion about this topic in the past,
perhaps others can shed more light on it.  It is entirely
possible that I have misremembered.  I do know that I
have had problems in the past updating progress bars
from other threads.

> Gtk.on_idle is missing from the ruby gnome 2 api documentation :S lol,
> can you up an example please?

Sorry.  Gtk.idle_add() and Gtk.idle_remove().  I'm a bit sleepy 
tonight...
Now that I understand what you are trying to do, replacing your
loop with

gtk.idle_add do
    if fase != oldfase
        g.pulsar(fase)
        oldfase = fase
    end
end
gtk.main

might at least get rid of the warning message.  Possibly moving
the require gtk2 into the thread may help.  At the very least
your code should quit when you catch the sigint since gtk.quit
will stop the main loop.

But it occurs to me that if you aren't interested in writing a
gui, but are only using the progress bar, perhaps you
don't need a main loop in the UI at all.  Instead of a
float that you are constantly monitoring, you can have
a progress object.  You run a method on the progress
object that increments the value and runs
Gtk.main_iteration_do(false) to update the UI (no
need to check for events, just don't block).  It will
cut down on the number of threads anyway... ;-)

          MikeC
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.