This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author syeberman
Recipients syeberman
Date 2011-05-25.19:58:13
SpamBayes Score 0.0
Marked as misclassified No
Message-id <1306353494.72.0.953237135168.issue12179@psf.upfronthosting.co.za>
In-reply-to
Content
I'm wanting to call PyThreadState_SetAsyncExc from a function registered with SetConsoleCtrlHandler.  To do so, I need to call PyGILState_Ensure, which asserts that Python is initialized, so I need to check for that.  However, I noticed a race condition with the code:

  if( Py_IsInitialized( ) ) {
    // XXX What if another thread calls Py_Finalize here?
    gstate = PyGILState_Ensure( );
    PyThreadState_SetAsyncExc( MainThreadId, PyExc_SystemExit );
    PyGILState_Release( gstate );
  }

What I need is to be able to hold the GIL around the entire block of code, potentially before Py_Initialize is called for the first time.  Now, 3.2 deprecated PyEval_AcquireLock, and PyEval_InitThreads can no longer be called before Py_Initialize.  Thankfully, I'm on 2.6.4, so I was able to write this code:

  PyEval_AcquireLock( );
  if( Py_IsInitialized( ) ) {
    gstate = PyGILState_Ensure( );
    PyThreadState_SetAsyncExc( MainThreadId, PyExc_SystemExit );
    PyGILState_Release( gstate );
  }
  PyEval_ReleaseLock( );

The problem in 2.6.4 is that PyGILState_Ensure deadlocks because the GIL is already held, so that doesn't solve my problem.  (Incidentally, the PyGILState_Ensure docs say it works "regardless of the current state of the GIL", which is incorrect.)

The 3.2 docs say to use PyEval_AcquireThread or PyEval_RestoreThread, which both require an existing PyThreadState.  To get that, I would need to call PyThreadState_New, which needs a PyInterpreterState.  To get _that_ I could use PyInterpreterState_Head, since I know I only use one interpreter.  Now I'm getting into "advanced debugger" territory, but it's no use anyway; it's possible that Py_Finalize could sneak in between the time I get this interpreter and when I acquire the GIL, causing me to access a free'd interpreter.

I believe the best fix for this would be to have a version of PyGILState_Ensure that works even when Python is not initialized.  It would not be able to create a thread, and thus I would not expect to be able to call any Python API, but it would always ensure the GIL is acquired.  This _may_ have to be a new "PyGILState_EnsureEx" function, because existing code expects PyGILState_Ensure to always allow them to call the Python API.  The resulting code would be:

  gstate = PyGILState_EnsureEx( );
  if( Py_IsInitialized( ) ) {
    PyThreadState_SetAsyncExc( MainThreadId, PyExc_SystemExit );
  }
  PyGILState_Release( gstate );

This would require that Py_Initialize itself acquires the GIL.

The above problem was found on 2.6.4, but I've consulted the 3.2 docs and 3.3 code (via the online source) and it looks like the situation would be exactly the same.  In the meantime, I'm going to stick with the first piece of code and hope nobody hits CTRL-BREAK during program clean-up.
History
Date User Action Args
2011-05-25 19:58:14syebermansetrecipients: + syeberman
2011-05-25 19:58:14syebermansetmessageid: <1306353494.72.0.953237135168.issue12179@psf.upfronthosting.co.za>
2011-05-25 19:58:14syebermanlinkissue12179 messages
2011-05-25 19:58:13syebermancreate