GNOME Bugzilla – Bug 547633
cannot create new threads when pygtk is used
Last modified: 2008-08-27 21:37:53 UTC
Please describe the problem: I have a simple test program that imports gobject and gtk, creates a new thread, and starts a main loop. The new thread prints a message and politely asks the main loop to exit. This program works in gnome 2.22 but I am told it hangs with the latest (as of yesterday) version of pygtk and the underlying libraries from svn. Steps to reproduce: Run the attached script using python from the command line. Actual results: The program gives no output and hangs indefinitely, indicating that the secondary thread does not run. Expected results: The program outputs "threads work" and quits immediately. Does this happen every time? yes Other information:
Created attachment 116507 [details] test program
Comment on attachment 116507 [details] test program >#!/usr/bin/env python >def main_quit(): > gtk.gdk.threads_enter() > try: > gtk.main_quit() > finally: > gtk.gdk.threads_leave() > >def nonmain_thread(): > print "threads work" > gobject.idle_add(main_quit) idle_add(func) will execute func in the main thread, so the threads_enter/leave shouldn't be necessary. Johan
that bit is not relevant for this bug (I tested with and without the enter/leave). That said, Vincent's code is correct IMHO: idle functions run in the main thread but are not protected by the gdk lock. See http://library.gnome.org/devel/gdk/unstable/gdk-Threads.html
Created attachment 116614 [details] test program 2 Strange. This bug apparently manifests itself only if gtk.main is used. If we create a custom glib.MainLoop it appears to work fine.
OK, I finally understood it, though it was a hell of debugging. This is another instance of compatibility breakage of PyGLib. In 'pygobject.h' there is this code: #define pyg_threads_enabled (_PyGObject_API->threads_enabled) #define pyg_begin_allow_threads \ G_STMT_START { \ PyThreadState *_save = NULL; \ if (_PyGObject_API->threads_enabled) \ _save = PyEval_SaveThread(); #define pyg_end_allow_threads \ if (_PyGObject_API->threads_enabled) \ PyEval_RestoreThread(_save); \ } G_STMT_END All three macros belong to the public interface. However, they now use variable (_PyGObject_API->threads_enabled) that is never updated and always remains FALSE. Moreover, PyGLib uses a function instead of a variable. I'm not quite sure how to fix this. pyglib_enable_threads() is a standalone public function, so it is not necessary be called from pygobject_enable_threads(). This means we cannot set _PyGObject_API->threads_enabled only in pygobject_enable_threads(), it is not the only entry point. Raising severity, since this breaks all thread stuff by rendering pyg_(begin|end)_allow_threads no-ops.
Created attachment 117368 [details] [review] unfinished fix This patch redefines pyg_* macros using pyglib_* macros and functions. However, it is unfinished, because after that PyGTK cannot be imported. Aparrently, libpyglib is not compiled in and thus library initialization fails at runtime. I don't understand this dependency mess. There is no pkg-config file for PyGLib?
Comment on attachment 117368 [details] [review] unfinished fix This looks pretty good, please commit
Created attachment 117473 [details] [review] working fix After discussion on IRC we concluded that _PyGObject_API->threads_enabled must always have the right value for backward compatibility. Since threads are enabled in a different library, that can be achieved only with some callback machinery. This patch adds it. Tested with the attached program: it works.
Sending ChangeLog Sending glib/pyglib.c Sending glib/pyglib.h Sending gobject/gobjectmodule.c Transmitting file data .... Committed revision 952. 2008-08-28 Paul Pogonyshev <pogonyshev@gmx.net> Bug 547633 – cannot create new threads when pygtk is used * glib/pyglib.c (pyglib_notify_on_enabling_threads): New function. (pyglib_enable_threads): Invoke all callbacks added with new pyglib_notify_on_enabling_threads(). * gobject/gobjectmodule.c (pyg_note_threads_enabled): New function (callback for new pyglib_notify_on_enabling_threads()). (PYGLIB_MODULE_START): Initialize 'pygobject_api_functions.threads_enabled' and also watch for thread being enabled later on.