When to call g_thread_init(), again...



The documentation for g_thread_init() says (in the stable branch):

  "You must call g_thread_init() before executing any other GLib
   functions in a threaded GLib program."

(The term "threaded GLib program" here is very vague. What it
efffectively means, to the best of my knowledge, is "a program that
calls g_thread_init() at any point at all". Specifically, it does
*not* mean "a program that actually creates threads and calls GLib API
from several threads", I think. In trunk I added more warnings to this
documentation. Read on.)

However, as has been pointed out before, unfortunately that
requirement isn't very effectively enforced, and especially on Linux
many programs that end up calling g_thread_init() after *tons* of
others (even quite complex) GLib API work just fine anyway. So nobody
cares, and few even know about that requirement. Except then when
something is changed in GLib in such a way that failing to meet the
requirement actually causes problems. Then GLib maintainers are quick
to point out that this requirement has been documented a long time,
and that properly written code should always have been written so that
it calls g_thread_init() first, if it will be called at all.

Now, it turns out that the following simple program hangs on Windows:

#include <glib.h>

static GStaticRecMutex rec_mutex = G_STATIC_REC_MUTEX_INIT;

int
main (int argc, char **argv)
{
  g_static_rec_mutex_lock (&rec_mutex);
  g_thread_init (NULL);
  g_static_rec_mutex_unlock (&rec_mutex);

  g_static_rec_mutex_lock (&rec_mutex);    /* Hangs in this call */
  g_static_rec_mutex_unlock (&rec_mutex);

  return  0;
}

(Note that no threads whatsoever are created.)

Clearly when distilled down to the above simple code, it is pretty
obvious that the call to g_thread_init() is done in a somewhat risky
place, and might cause odd effects. This is very obvious especially if
one knows that the g_static_rec_mutex_*() calls are NOPs if
g_thread_init() has not been called. (So what happens here is
effectively that the GStaticRecMutex gets unlocked without having been
locked, which screws up it royally in the Windows implementation.).

The real use case reported on gtk-list looked much more innocent,
though. Its main() started with:

  gtk_init (&argc, &args);

  html = gtk_html_new_from_string (html_source, -1);

How can the poor application programmer know that
gtk_html_new_from_string() will cause g_thread_init() to be called
deep in the bowels of libORBit2? What should be fixed? Should gtkhtml
be fixed to call g_thread_init() itself, from functions that might
conceivably be the first ones that user code calls? But still that
wouldn't help strictly speaking, as gtk_init() of course also calls
lots of GLib functions, so it is too late to call g_thread_init()
somewhere in libgtkhtml in this case. Should ORBit2 g_error() out if
it notices that it wants to use threads but g_thread_init() has not
been called, instead of calling it itself?

gtk_init() can hardly know if other libraries used by the program will
eventually call g_thread_init(), so it's impossible for it to know
whether to call g_thread_init().

So, should the requirement for g_thread_init() being called before any
other GLib API, if it is called at all, be removed? (After all,
failing it apparently is harmless anyway, on Linux. No idea about
other POSIX platforms.) I think that the GStaticRecMutex problem above
can be fixed on Windows in that case.

--tml


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