[Python-Dev] pystate.c changes for Python 2.4.2

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 Py_FatalError("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 PyThreadState_Delete (which is used when I
>>> destroy thread states and interpreters) is not making the same clean up
>>> that PyThreadState_DeleteCurrent 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 
>>> PyThreadState_Delete instead of PyThreadState_DeleteCurrent, then an 
>>> old and invalid value for autoTLSkey is still stored, and that is
>>> triggering the Py_FatalError when a call to PyThreadState_Swap is made.
>>> If I add this code at the end of PyThreadState_Delete:
>>>
>>> if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate)
>>>     PyThread_delete_key_value(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.
> Py_Initialize();
> PyEval_InitThreads();
> PyThreadState* p_main =  PyThreadState_Swap(NULL);
> PyEval_ReleaseLock();
> 
> /*
> 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.
> */
> {
>     PyEval_AcquireLock();
>     PyThreadState_Swap(p_main);
>     PyThreadState* p_child = PyThreadState_New(p_main->interp);
> 
>     PyThreadState_Swap(p_child);
>     PyRun_SimpleString("print 'mi vieja mula ya no es lo que era'");
> 
>     PyThreadState_Clear(p_child);
>     PyThreadState_Swap(NULL);
>     PyThreadState_Delete(p_child);
>     PyEval_ReleaseLock();
>     // The os level thread exits here.
> }
> /*
> When this thread state gets deleted (using PyThreadState_Delete), the 
> autoTLSkey stored for thread id number 10 is not removed, because 
> PyThreadState_Delete doesn't have the necesary cleanup code (check 
> pystate.c):
> 
> if (autoTLSkey && PyThread_get_key_value(autoTLSkey) == tstate)
>     PyThread_delete_key_value(autoTLSkey);
> 
> PyThreadState_DeleteCurrent behaves correctly because it does have this 
> code.
> I think that this code should be added to tstate_delete_common (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 Py_FatalError I'm talking about.
> A workaround for this would be to use PyThreadState_DeleteCurrent 
> instead of PyThreadState_Delete.
> 
> If you use the following code instead of the one above, the you have no 
> way out to the Py_FatalError:
> */
> {
>     PyEval_AcquireLock();
>     PyThreadState* ret = Py_NewInterpreter();
> 
>     PyRun_SimpleString("print 'mi vieja mula ya no es lo que era'");
> 
>     Py_EndInterpreter(ret);
>     PyThreadState_Swap(NULL);
>     PyEval_ReleaseLock();
> }
> /*
> When this interpreter gets deleted (using Py_EndInterpreter), its thread 
> state gets deleted using PyThreadState_Delete, 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