[Python-Dev] GIL vs thread state
Guido van Rossum
guido@python.org
Mon, 14 Apr 2003 11:18:07 -0400
> > If pts->frame is not NULL, I'd expect a warning from
> > PyThreadState_Clear(): "PyThreadState_Clear: warning: thread still has
> > a frame\n".
> You mean this code, from Python/pystate.h?
>
> void
> PyThreadState_Clear(PyThreadState *tstate)
> {
> if (Py_VerboseFlag && tstate->frame != NULL)
> fprintf(stderr,
> "PyThreadState_Clear: warning: thread still has a frame\n");
>
> ZAP(tstate->frame);
>
> ZAP(tstate->dict);
> ...
> }
Yes.
> Py_VerboseFlag is 0 set in my case, so no warning is printed.
OK.
> > > "Fatal Python error: PyThreadState_Get: no current thread" in the call
> > > to PyThreadState_Clear().
> >
> > That's strange, because I cannot trace the code in there to such a
> > call. (Unless it is in a destructor.
>
> It is in a destructor: frame_dealloc, called from ZAP(tstate->frame).
Aha. That wasn't obvious from your description.
> > Can you tell more about where the PyThreadState_Get() call was?)
>
> This function allocates the threadstate for me:
>
> static void EnterPython(char *msg)
> {
> PyThreadState *pts;
> PyEval_AcquireLock();
> pts = PyThreadState_New(g_interp);
> if (!pts)
> Py_FatalError("wincall: Could not allocate ThreadState");
> if (NULL != PyThreadState_Swap(pts))
> Py_FatalError("wincall (EnterPython): thread state not == NULL?");
> }
Maybe you should have a look at Mark Hammond's PEP 311. It describes
the problem and proposes a better solution. (I think it requires you
to always use the existing thread state for the thread, rather than
making up a temporary thread state as is currently the idiom.)
> To explain the picture a little better, here is the sequence of calls:
>
> Python calls into a C extension.
> The C extension does
>
> Py_BEGIN_ALLOW_THREADS
> call_a_C_function()
> Py_END_ALLOW_THREADS
>
> The call_a_C_function calls back into C code like this:
>
> void MyCallback(void)
> {
> EnterPython(); /* acquire the lock, and create a thread state */
> execute_some_python_code();
> LeavePython(); /* destroy the thread state, and release the lock */
> }
>
> Now, the execute_some_python_code() section is enclosed in a win32
> structured exception handling block, and it may return still with a
> frame in the threadstate, as it seems.
Ouch! I don't know what structured exception handling is, but this
looks like it would be as bad as using setjmp/longjmp to get back to
right after execute_some_python_code(). That code could leak
arbitrary Python references!!!
> Oops, I just tried the code in CVS python, and the problem goes away.
> Same for 2.3a2.
I vaguely recall that someone fixed some things in this area... :-(
> But my code has to run in 2.2.2 as well...
If the docs are lying, they have to be fixed. This is no longer my
prime area of expertise... :-(
--Guido van Rossum (home page: http://www.python.org/~guido/)