[Python-Dev] pystate.c changes for Python 2.4.2 (original) (raw)

Gabriel Becedillas lists at core-sdi.com
Mon Jan 16 17:27:11 CET 2006


Gabriel Becedillas wrote:

Michael Hudson wrote:

Gabriel Becedillas <gabriel.becedillas at corest.com> writes:

Hi, At the company I work for, we've embedded Python in C++ application we develop. Since our upgrade to Python 2.4.2 from 2.4.1 we started hitting PyFatalError("Invalid thread state for this thread") when using debug builds. We use both multiple interpreters and thread states. I know you've said this to me in email already, but I have no idea how you managed to get this to work with 2.4.1 :) I think the problem is that PyThreadStateDelete (which is used when I destroy thread states and interpreters) is not making the same clean up that PyThreadStateDeleteCurrent is doing (which is used when we create threads from python code). If a thread id is reused (obviously not between two running threads), and the previous thread used PyThreadStateDelete instead of PyThreadStateDeleteCurrent, then an old and invalid value for autoTLSkey is still stored, and that is triggering the PyFatalError when a call to PyThreadStateSwap is made. If I add this code at the end of PyThreadStateDelete: if (autoTLSkey && PyThreadgetkeyvalue(autoTLSkey) == tstate) PyThreaddeletekeyvalue(autoTLSkey); then everything works fine. I think I begin to understand the problem... but this is still fragile: it relies on the fact that you delete a thread state from the OS level thread that created it, but while a thread belonging to a different interpreter state has the GIL (or at least: the interpreter state of the thread state being deleted doesn't hold the GIL). Can you guarantee that? Mmm... it doesn't have to do with a race condition or something. Let me try to show you with an example (keep in mind that this relies on the fact that at some moment the operating system gives you a thread with the same id of another thread that allready finished executing): // Initialize python. PyInitialize(); PyEvalInitThreads(); PyThreadState* pmain = PyThreadStateSwap(NULL); PyEvalReleaseLock(); /* Asume this block is executed in a separate thread, and that has been asigned thread id 10. It doesn't matter what the main thread (the thread that initialized Python) is doing. Lets assume its waiting for this one to finish. */ { PyEvalAcquireLock(); PyThreadStateSwap(pmain); PyThreadState* pchild = PyThreadStateNew(pmain->interp); PyThreadStateSwap(pchild); PyRunSimpleString("print 'mi vieja mula ya no es lo que era'"); PyThreadStateClear(pchild); PyThreadStateSwap(NULL); PyThreadStateDelete(pchild); PyEvalReleaseLock(); // The os level thread exits here. } /* When this thread state gets deleted (using PyThreadStateDelete), the autoTLSkey stored for thread id number 10 is not removed, because PyThreadStateDelete doesn't have the necesary cleanup code (check pystate.c): if (autoTLSkey && PyThreadgetkeyvalue(autoTLSkey) == tstate) PyThreaddeletekeyvalue(autoTLSkey); PyThreadStateDeleteCurrent behaves correctly because it does have this code. I think that this code should be added to tstatedeletecommon (I've done this and everything worked just fine). If a new thread executes the same code, and that thread is assigned the same thread id, you get the PyFatalError I'm talking about. A workaround for this would be to use PyThreadStateDeleteCurrent instead of PyThreadStateDelete. If you use the following code instead of the one above, the you have no way out to the PyFatalError: */ { PyEvalAcquireLock(); PyThreadState* ret = PyNewInterpreter(); PyRunSimpleString("print 'mi vieja mula ya no es lo que era'"); PyEndInterpreter(ret); PyThreadStateSwap(NULL); PyEvalReleaseLock(); } /* When this interpreter gets deleted (using PyEndInterpreter), its thread state gets deleted using PyThreadStateDelete, and you are back to the beginning of the problem. */ I hope this helps to clarify the problem. Thanks a lot and have a nice day. It seems to me that to be robust, you need a way of saying "delete the thread local value of autoTLSKey from thread $FOO". But this is all very confusing... Could you please confirm if this is a bug ? Yes, I think it's a bug. Cheers, mwh

Can anybody tell me if the patch I suggested is ok ? That will be to add the following code at the end of PyThreadState_Delete:

if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate) PyThread_delete_key_value(autoTLSkey);

Thank you very much.

--

Gabriel Becedillas Developer CORE SECURITY TECHNOLOGIES

Florida 141 - 2º cuerpo - 7º piso C1005AAC Buenos Aires - Argentina Tel/Fax: (54 11) 5032-CORE (2673) http://www.corest.com



More information about the Python-Dev mailing list