Re: Threads



John K Luebs wrote:
On Tue, Dec 30, 2003 at 02:55:38AM +1100, Russell Shaw wrote:

Hi,
In a multi-threaded gtk app, i get errors like:

 Xlib: unexpected async reply (sequence 0x3170)!


I tried this mutex method to stop non-reentrant gdk things
from being screwed:

void draw(GdkDrawable *drawable, GdkGC *gc, gint32 x, gint32 y, gint size)
{
   static GStaticMutex mutex=G_STATIC_MUTEX_INIT;
....
   g_static_mutex_lock(&mutex);
   gdk_draw_rectangle(drawable,gc,TRUE,x-size/2,y-size/2,size,size);
   g_static_mutex_unlock(&mutex);
}

I didn't use gdk_threads_enter()/leave() because this blocks when
it is called from the 'normal' gtk_main thread.


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()?




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