Kaydet (Commit) 1d5ad90c authored tarafından Guido van Rossum's avatar Guido van Rossum

CRITICAL PATCH!

We occasionally received reports from people getting "invalid tstate"
crashes (this is a fatal error in PyThreadState_Delete()).  Finally
several people were able to reproduce it reliably and Tim Peters
discovered that there is a race condition when multiple threads are
calling this function without holding the global interpreter lock (the
function may be called without holding that).

Solved the race condition by adding a lock around the mutating uses of
interp->tstate_head.  Tim and Jonathan Giddy have run tests that make
it likely that this fixes the crashes -- although Tim hasn't heard
from the person who reported the original problem.
üst 7f851869
...@@ -40,6 +40,18 @@ PERFORMANCE OF THIS SOFTWARE. ...@@ -40,6 +40,18 @@ PERFORMANCE OF THIS SOFTWARE.
} }
#ifdef WITH_THREAD
#include "pythread.h"
static PyThread_type_lock head_mutex = NULL; /* Protects interp->tstate_head */
#define HEAD_INIT() (head_mutex || (head_mutex = PyThread_allocate_lock()))
#define HEAD_LOCK() PyThread_acquire_lock(head_mutex, WAIT_LOCK)
#define HEAD_UNLOCK() PyThread_release_lock(head_mutex)
#else
#define HEAD_INIT() /* Nothing */
#define HEAD_LOCK() /* Nothing */
#define HEAD_UNLOCK() /* Nothing */
#endif
static PyInterpreterState *interp_head = NULL; static PyInterpreterState *interp_head = NULL;
PyThreadState *_PyThreadState_Current = NULL; PyThreadState *_PyThreadState_Current = NULL;
...@@ -51,6 +63,7 @@ PyInterpreterState_New() ...@@ -51,6 +63,7 @@ PyInterpreterState_New()
PyInterpreterState *interp = PyMem_NEW(PyInterpreterState, 1); PyInterpreterState *interp = PyMem_NEW(PyInterpreterState, 1);
if (interp != NULL) { if (interp != NULL) {
HEAD_INIT();
interp->modules = NULL; interp->modules = NULL;
interp->sysdict = NULL; interp->sysdict = NULL;
interp->builtins = NULL; interp->builtins = NULL;
...@@ -70,8 +83,10 @@ PyInterpreterState_Clear(interp) ...@@ -70,8 +83,10 @@ PyInterpreterState_Clear(interp)
PyInterpreterState *interp; PyInterpreterState *interp;
{ {
PyThreadState *p; PyThreadState *p;
HEAD_LOCK();
for (p = interp->tstate_head; p != NULL; p = p->next) for (p = interp->tstate_head; p != NULL; p = p->next)
PyThreadState_Clear(p); PyThreadState_Clear(p);
HEAD_UNLOCK();
ZAP(interp->modules); ZAP(interp->modules);
ZAP(interp->sysdict); ZAP(interp->sysdict);
ZAP(interp->builtins); ZAP(interp->builtins);
...@@ -82,12 +97,11 @@ static void ...@@ -82,12 +97,11 @@ static void
zapthreads(interp) zapthreads(interp)
PyInterpreterState *interp; PyInterpreterState *interp;
{ {
PyThreadState *p, *q; PyThreadState *p;
p = interp->tstate_head; /* No need to lock the mutex here because this should only happen
while (p != NULL) { when the threads are all really dead (XXX famous last words). */
q = p->next; while ((p = interp->tstate_head) != NULL) {
PyThreadState_Delete(p); PyThreadState_Delete(p);
p = q;
} }
} }
...@@ -139,8 +153,10 @@ PyThreadState_New(interp) ...@@ -139,8 +153,10 @@ PyThreadState_New(interp)
tstate->sys_profilefunc = NULL; tstate->sys_profilefunc = NULL;
tstate->sys_tracefunc = NULL; tstate->sys_tracefunc = NULL;
HEAD_LOCK();
tstate->next = interp->tstate_head; tstate->next = interp->tstate_head;
interp->tstate_head = tstate; interp->tstate_head = tstate;
HEAD_UNLOCK();
} }
return tstate; return tstate;
...@@ -185,6 +201,7 @@ PyThreadState_Delete(tstate) ...@@ -185,6 +201,7 @@ PyThreadState_Delete(tstate)
interp = tstate->interp; interp = tstate->interp;
if (interp == NULL) if (interp == NULL)
Py_FatalError("PyThreadState_Delete: NULL interp"); Py_FatalError("PyThreadState_Delete: NULL interp");
HEAD_LOCK();
for (p = &interp->tstate_head; ; p = &(*p)->next) { for (p = &interp->tstate_head; ; p = &(*p)->next) {
if (*p == NULL) if (*p == NULL)
Py_FatalError( Py_FatalError(
...@@ -193,6 +210,7 @@ PyThreadState_Delete(tstate) ...@@ -193,6 +210,7 @@ PyThreadState_Delete(tstate)
break; break;
} }
*p = tstate->next; *p = tstate->next;
HEAD_UNLOCK();
PyMem_DEL(tstate); PyMem_DEL(tstate);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment