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 735142 - set_interactive(1) (default) makes flawed locking assumptions and breaks with GLib 2.41
set_interactive(1) (default) makes flawed locking assumptions and breaks with...
Status: RESOLVED DUPLICATE of bug 690740
Product: pygtk
Classification: Bindings
Component: gtk
2.24.x
Other Linux
: Normal major
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2014-08-21 08:47 UTC by Simon McVittie
Modified: 2014-08-21 09:18 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Simon McVittie 2014-08-21 08:47:07 UTC
PyGtk uses PyOS_InputHook to iterate the GLib main loop while in the interactive Python prompt, unless set_interactive(0) has been called.

However, I can't see how the locking is meant to work. The PyOS_InputHook implementation, "_loop", calls:

    /* Start the main loop */
    gstate = PyGILState_Ensure();
    gtk_main();
    PyGILState_Release(gstate);

so it is invoking gtk_main() with the GDK mutex in an undefined state: it might be locked, it might be unlocked, nobody knows. It depends who calls input() and where.

Gtk2's gtk_main() is, as far as I'm aware, one of the functions that according to their documentation, must only be called with the GDK mutex locked. In particular, it does this:

  if (g_main_loop_is_running (main_loops->data))
    {
      GDK_THREADS_LEAVE ();            /* i.e. unlock */
      g_main_loop_run (loop);
      GDK_THREADS_ENTER ();            /* i.e. lock */
      gdk_flush ();
    }

On entry, there are three possibilities: this thread has the lock, another thread has the lock, or nobody has the lock.

If this thread was holding the lock on entry, it is holding it on exit. Fine.

If nothing was holding the lock on entry, GDK_THREADS_LEAVE () unlocks an already-unlocked mutex, which happened to "work" on GLib <= 2.40 on Linux, but is a fatal error on FreeBSD (Bug #678758) and on GLib 2.41+ on Linux (Bug #735141). If the application survives that, GDK_THREADS_ENTER () locks the mutex. Now the thread that invoked input() is holding the GDK mutex. If that thread did not intend to be the GUI thread, it will probably never release it except via _loop(), leading to the GUI thread being blocked at all times other than when input() happens to be active. If that thread *is* the GUI thread but the GDK mutex happens to be non-recursive (it is undefined whether it is recursive or not), then it will block forever when the GUI resumes execution.

If the lock was held on entry by another thread, that's a fatal error on FreeBSD according to Bug #678758. On other platforms, the lock is unlocked, but the other thread that thought it held the lock no longer does, and will continue to do GUI things that are no longer thread-safe, potentially leading to some sort of crash when a callback from g_main_loop_run() takes the lock and also does GUI things.

Debian's reportbug tool is one example of this happening "in real life" (<https://bugs.debian.org/758619>). I've suggested that reportbug should call set_interactive(0) as a workaround.
Comment 1 Simon McVittie 2014-08-21 09:18:05 UTC
Eric Valette pointed out on the Debian bug that this had already been filed as a kFreeBSD-specific bug, but since 2.41 it affects Linux too.

*** This bug has been marked as a duplicate of bug 690740 ***