After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 547633 - cannot create new threads when pygtk is used
cannot create new threads when pygtk is used
Status: RESOLVED FIXED
Product: pygobject
Classification: Bindings
Component: general
Git master
Other All
: Urgent blocker
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2008-08-13 16:43 UTC by Vincent Povirk
Modified: 2008-08-27 21:37 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
test program (371 bytes, text/plain)
2008-08-13 16:44 UTC, Vincent Povirk
  Details
test program 2 (509 bytes, text/plain)
2008-08-14 21:20 UTC, Paul Pogonyshev
  Details
unfinished fix (1.87 KB, patch)
2008-08-25 21:17 UTC, Paul Pogonyshev
needs-work Details | Review
working fix (2.75 KB, patch)
2008-08-27 18:58 UTC, Paul Pogonyshev
committed Details | Review

Description Vincent Povirk 2008-08-13 16:43:33 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:
Comment 1 Vincent Povirk 2008-08-13 16:44:07 UTC
Created attachment 116507 [details]
test program
Comment 2 Johan (not receiving bugmail) Dahlin 2008-08-13 16:58:33 UTC
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
Comment 3 Paolo Borelli 2008-08-13 17:10:36 UTC
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
Comment 4 Paul Pogonyshev 2008-08-14 21:20:54 UTC
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.
Comment 5 Paul Pogonyshev 2008-08-25 20:36:48 UTC
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.
Comment 6 Paul Pogonyshev 2008-08-25 21:17:04 UTC
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 7 Johan (not receiving bugmail) Dahlin 2008-08-26 21:13:24 UTC
Comment on attachment 117368 [details] [review]
unfinished fix

This looks pretty good, please commit
Comment 8 Paul Pogonyshev 2008-08-27 18:58:57 UTC
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.
Comment 9 Paul Pogonyshev 2008-08-27 21:37:53 UTC
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.