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 302668 - Calling gobject.threads_init() after gobject.MainLoop.run causes gobject.MainLoop.quit to abort the Python interpreter
Calling gobject.threads_init() after gobject.MainLoop.run causes gobject.Main...
Status: RESOLVED OBSOLETE
Product: pygobject
Classification: Bindings
Component: gobject
2.9.0
Other Linux
: Normal normal
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2005-05-01 21:43 UTC by Jp Calderone
Modified: 2012-04-21 14:13 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Jp Calderone 2005-05-01 21:43:58 UTC
Distribution/Version: Ubuntu Hoary

Here's a Python program which demonstrates this behavior:

# Starts
import pygtk
pygtk.require('2.0')
import gobject

loop = gobject.MainLoop()

def breakIt():
    gobject.threads_init()
    loop.quit()

gobject.timeout_add(250, breakIt)
loop.run()
# Ends

One possible solution to this might be to redefine pyg_end_allow_threads as follows:

#define pyg_end_allow_threads                   \
        if (_save != NULL)                      \
            PyEval_RestoreThread(_save);        \
    } G_STMT_END

This would prevent PyEval_RestoreThread from being invoked with a bogus value in
the case where threads are initialized between the call to run and quit (Note, I
have not tested that this actually fixes anything, nor that it does not
introduce other problems).

An even cooler solution would have threads_init retroactively initialize the
Python threadstate of all mainloops started previously, letting them cooperative
with other Python threads and still cleanup after themselves.  I don't know what
a solution of this form would look like.
Comment 1 Gustavo Carneiro 2005-07-10 10:52:16 UTC
Anyone else with a clue about threads care to comment on the proposed solution?
Comment 2 Johan (not receiving bugmail) Dahlin 2006-01-09 14:27:30 UTC
gobject.threads_init() should *always* be called before entering the mainloop.

JP, is there a good reason you can't modify your application to call gobject.threads_init() earlier?
Comment 3 Jp Calderone 2006-01-09 15:12:05 UTC
Maybe.  Currently, I call threads_init() unconditionally at start up time.  Hopefully, my code gets run before anyone does anything with gobject, but I can't really guarantee or require that.  Even if this works out perfectly, though, it still means I am initializing threads when there is no reason to.  I assume there is some penalty associated with doing so, since gobject doesn't do it automatically itself.  Ideally, I should be able to avoid initializing threads until I actually decide to start a thread.

Those matters aside, Python wrappers shouldn't expose functionality which can cause the Python interpreter to exit abnormally.  The demonstration program causes Python to exit with an abort.

It would even be an improvement if threads_init raised an exception instead of initializing threads when a mainloop is already active.  Of course, I would still prefer the 2nd solution mentioned in the original report.
Comment 4 Johan (not receiving bugmail) Dahlin 2006-01-09 16:42:20 UTC
Okay, thanks for the clarification, what we have are essentially two use cases:

1) Programs which might use threads at some point, but doesn't know when gobject is imported/used.

2) Programs who wants to use threads but do not wish to use all the resources initializing the thread sub system requires.

It's unfortunately quite easy to make pygtk crash, but ideally I agree that it should not be possible.

This bug may be irrelevent if bug 162623 is closed.
Comment 5 Paul Pogonyshev 2008-09-09 20:31:54 UTC
As a side note, doing as bug reporter suggests will also give a little faster and smaller code I think.
Comment 6 Simon van der Linden 2011-01-18 16:25:54 UTC
When I run the test case today, I get:

python: /builddir/build/BUILD/Python-2.7/Python/pystate.c:644: PyGILState_Release: Assertion `oldstate == PyGILState_UNLOCKED' failed.

Program received signal SIGABRT, Aborted.

First, MainLoop.quit() does not cause any problem. The problem is that pyglib_gil_state_ensure doesn't lock the GIL when threads are disabled. In _pyglib_handler_marshal, pyglib_gil_state_ensure is called before the Python callback is called, and pyglib_gil_state_release is called after. The thing is, the latter try to release the lock that wasn't locked, since threads were enabled in the middle.

A simple solution is to keep the number of calls to pyglib_gil_state_ensure, and to lock the GIL as many times as the counter reads when threads are enabled.
Comment 7 Sebastian Pölsterl 2012-04-21 14:13:57 UTC
Thanks for taking the time to report this bug. However, this bug report applies to a PyGObject version that is too old and not supported anymore.
PyGObject developers are no longer working on static bindings, so unfortunately there will not be any bug fixes for the version this report applies to.

By upgrading to a newer version of PyGObject you could receive bug fixes and new functionality. Please feel free to reopen this bug if the problem still occurs with a newer version of PyGObject.