GNOME Bugzilla – Bug 302668
Calling gobject.threads_init() after gobject.MainLoop.run causes gobject.MainLoop.quit to abort the Python interpreter
Last modified: 2012-04-21 14:13:57 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.
Anyone else with a clue about threads care to comment on the proposed solution?
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?
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.
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.
As a side note, doing as bug reporter suggests will also give a little faster and smaller code I think.
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.
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.