Re: Threads



On Tue, Dec 30, 2003 at 07:41:55PM +1100, Russell Shaw wrote:
John K Luebs wrote:
Then you get what you deserve :-).
BTW, what did you think gdk_threads_enter/leave were written for?
If your app is blocking you're simply using them incorrectly.

Please refer to Common Question 1.5 in the GTK docs.

threads_enter acquires a lock, and threads_leave releases
the lock. This lock exists for the benefit of both Xlib and GTK+. GTK+
authors and module writers assume that this lock is held (why shouldn't
they, its part of the documented "contract"), so an Xlib: unexpected 
async reply may not be the only problem you will experience if you don't
heed my advice to lock properly.

The rule is that you need to have acquired the lock before you call ANY
GTK or GDK function. Luckily, the documentation clearly spells out 
where you will and won't have the lock. Obviously, when you start in
main(), you don't have the lock so you have to do the threads_init
and then threads_enter before entering the GTK mainloop. When GTK/GDK
hands control off to the Glib mainloop it drops the lock because Glib
is fully threadsafe. Since timeouts, I/O watches, and idle handlers
are called from glib, you must acquire the lock before you call any
GTK/GDK functions from one of these handlers. If your timeout/I/O/idle
handler does not call any GDK/GTK functions you do not need to acquire
the lock (This should be self evident, but I find many examples of 
people who clearly do not understand what this locking thing is about
and sprinkle their code with the u/v pairs until the thing works for 
them, and leave a deadlock/race prone mess).
Signals, on the other hand, are always called from GTK, so the
lock will already be held when you enter the signal handler and you
should not attempt to acquire it. You follow these simple rules, and
your program will not only work, it will be correct.

The result of this is that it is almost never correct to pass the same
handler to a signal_connect and a timeout/io/idle callback. The latter
case will require that you pass a handler that does the necessary lock
acquire/release before calling the gtk calling function. This should be
no big deal.

Read the docs I referred you to carefully. If you have further problems
feel free to mail the list, but if you're trying to avoid
threads_enter/threads_leave you are doing something wrong.

Thanks. I've read all those docs before. One thing i didn't do is use
gdk_threads_enter()/leave() within main():

int main (int argc, char *argv[])
{
  /* init threads */
  g_thread_init (NULL);
  gdk_threads_init ();

  /* init gtk */
  gtk_init(&argc, &argv);
...

  gdk_threads_enter ();
  gtk_main ();
  gdk_threads_leave ();

  return 0;
}

Why do i need that if main() is only run once and 'sits' in gtk_main()?


Well, gtk_main is a gtk function so, being consistent, assumes that you
have the lock held. Remember that gtk_main touches gtk data structures
and can cause signals to fire, so the lock must be acquired. The main
entry point is just another thread like any other, so you won't just
have the lock acquired for you.

Why doesn't gtk_main acquire the lock for you then? Well, look at the
docs on gtk_main. The gtk_main function can be called recursively. In
fact it is called recursively from gtk_dialog_run, for example. So, if
you wanted to call gtk_main from an idle handler you would have to
enclose it in an enter/leave pair, but if you were going to call
gtk_main from a signal handler (or you were running a modal dialog) you
would not need the pair because your signal handlers should assume that
the lock is acquired as a precondition.

--jkl




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]