Gtk and threaded scripts - compatible?

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:

obviously still in an unusable state :slight_smile:

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 :wink:
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… :wink:

       MikeC

Mike C. 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?

Mike C. 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. :slight_smile:

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 :slight_smile: – 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. :stuck_out_tongue:

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… :wink:

       MikeC

thanks Mike

On 16 May 2010 23:43, Roberto Cm [email protected]
wrote:

one of which actually work. :stuck_out_tongue:
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… :wink:

      MikeC