mirror of
https://github.com/python/cpython.git
synced 2024-12-01 11:15:56 +01:00
412f246024
can cause it to get called by multiple threads simultaneously. Ditto for PyInterpreterState_Delete. Of the former, the docs say "The interpreter lock need not be held, but may be held if it is necessary to serialize calls to this function". This kinda implies it both is and isn't thread-safe. Of the latter, the docs merely say "The interpreter lock need not be held.", and the clause about serializing is absent. I expect it was *believed* these are both thread-safe, and the bit about serializing via the global lock was meant as a permission rather than a caution. I also expect we've never seen a problem here because the Python core (prior to the _PyPclose fix) only calls these functions once per run. The Py_NewInterpreter subsystem exposed by the C API (but not used by Python itself) also calls them, but that subsystem appears to be very rarely used. Whatever, they're both thread-safe now.
222 lines
4.7 KiB
C
222 lines
4.7 KiB
C
|
|
/* Thread and interpreter state structures and their interfaces */
|
|
|
|
#include "Python.h"
|
|
|
|
#define ZAP(x) { \
|
|
PyObject *tmp = (PyObject *)(x); \
|
|
(x) = NULL; \
|
|
Py_XDECREF(tmp); \
|
|
}
|
|
|
|
|
|
#ifdef WITH_THREAD
|
|
#include "pythread.h"
|
|
static PyThread_type_lock head_mutex = NULL; /* Protects interp->tstate_head */
|
|
#define HEAD_INIT() (void)(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;
|
|
|
|
PyThreadState *_PyThreadState_Current = NULL;
|
|
|
|
|
|
PyInterpreterState *
|
|
PyInterpreterState_New(void)
|
|
{
|
|
PyInterpreterState *interp = PyMem_NEW(PyInterpreterState, 1);
|
|
|
|
if (interp != NULL) {
|
|
HEAD_INIT();
|
|
interp->modules = NULL;
|
|
interp->sysdict = NULL;
|
|
interp->builtins = NULL;
|
|
interp->checkinterval = 10;
|
|
interp->tstate_head = NULL;
|
|
|
|
HEAD_LOCK();
|
|
interp->next = interp_head;
|
|
interp_head = interp;
|
|
HEAD_UNLOCK();
|
|
}
|
|
|
|
return interp;
|
|
}
|
|
|
|
|
|
void
|
|
PyInterpreterState_Clear(PyInterpreterState *interp)
|
|
{
|
|
PyThreadState *p;
|
|
HEAD_LOCK();
|
|
for (p = interp->tstate_head; p != NULL; p = p->next)
|
|
PyThreadState_Clear(p);
|
|
HEAD_UNLOCK();
|
|
ZAP(interp->modules);
|
|
ZAP(interp->sysdict);
|
|
ZAP(interp->builtins);
|
|
}
|
|
|
|
|
|
static void
|
|
zapthreads(PyInterpreterState *interp)
|
|
{
|
|
PyThreadState *p;
|
|
/* No need to lock the mutex here because this should only happen
|
|
when the threads are all really dead (XXX famous last words). */
|
|
while ((p = interp->tstate_head) != NULL) {
|
|
PyThreadState_Delete(p);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
PyInterpreterState_Delete(PyInterpreterState *interp)
|
|
{
|
|
PyInterpreterState **p;
|
|
zapthreads(interp);
|
|
HEAD_LOCK();
|
|
for (p = &interp_head; ; p = &(*p)->next) {
|
|
if (*p == NULL)
|
|
Py_FatalError(
|
|
"PyInterpreterState_Delete: invalid interp");
|
|
if (*p == interp)
|
|
break;
|
|
}
|
|
if (interp->tstate_head != NULL)
|
|
Py_FatalError("PyInterpreterState_Delete: remaining threads");
|
|
*p = interp->next;
|
|
HEAD_UNLOCK();
|
|
PyMem_DEL(interp);
|
|
}
|
|
|
|
|
|
PyThreadState *
|
|
PyThreadState_New(PyInterpreterState *interp)
|
|
{
|
|
PyThreadState *tstate = PyMem_NEW(PyThreadState, 1);
|
|
|
|
if (tstate != NULL) {
|
|
tstate->interp = interp;
|
|
|
|
tstate->frame = NULL;
|
|
tstate->recursion_depth = 0;
|
|
tstate->ticker = 0;
|
|
tstate->tracing = 0;
|
|
|
|
tstate->dict = NULL;
|
|
|
|
tstate->curexc_type = NULL;
|
|
tstate->curexc_value = NULL;
|
|
tstate->curexc_traceback = NULL;
|
|
|
|
tstate->exc_type = NULL;
|
|
tstate->exc_value = NULL;
|
|
tstate->exc_traceback = NULL;
|
|
|
|
tstate->sys_profilefunc = NULL;
|
|
tstate->sys_tracefunc = NULL;
|
|
|
|
HEAD_LOCK();
|
|
tstate->next = interp->tstate_head;
|
|
interp->tstate_head = tstate;
|
|
HEAD_UNLOCK();
|
|
}
|
|
|
|
return tstate;
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
ZAP(tstate->curexc_type);
|
|
ZAP(tstate->curexc_value);
|
|
ZAP(tstate->curexc_traceback);
|
|
|
|
ZAP(tstate->exc_type);
|
|
ZAP(tstate->exc_value);
|
|
ZAP(tstate->exc_traceback);
|
|
|
|
ZAP(tstate->sys_profilefunc);
|
|
ZAP(tstate->sys_tracefunc);
|
|
}
|
|
|
|
|
|
void
|
|
PyThreadState_Delete(PyThreadState *tstate)
|
|
{
|
|
PyInterpreterState *interp;
|
|
PyThreadState **p;
|
|
if (tstate == NULL)
|
|
Py_FatalError("PyThreadState_Delete: NULL tstate");
|
|
if (tstate == _PyThreadState_Current)
|
|
Py_FatalError("PyThreadState_Delete: tstate is still current");
|
|
interp = tstate->interp;
|
|
if (interp == NULL)
|
|
Py_FatalError("PyThreadState_Delete: NULL interp");
|
|
HEAD_LOCK();
|
|
for (p = &interp->tstate_head; ; p = &(*p)->next) {
|
|
if (*p == NULL)
|
|
Py_FatalError(
|
|
"PyThreadState_Delete: invalid tstate");
|
|
if (*p == tstate)
|
|
break;
|
|
}
|
|
*p = tstate->next;
|
|
HEAD_UNLOCK();
|
|
PyMem_DEL(tstate);
|
|
}
|
|
|
|
|
|
PyThreadState *
|
|
PyThreadState_Get(void)
|
|
{
|
|
if (_PyThreadState_Current == NULL)
|
|
Py_FatalError("PyThreadState_Get: no current thread");
|
|
|
|
return _PyThreadState_Current;
|
|
}
|
|
|
|
|
|
PyThreadState *
|
|
PyThreadState_Swap(PyThreadState *new)
|
|
{
|
|
PyThreadState *old = _PyThreadState_Current;
|
|
|
|
_PyThreadState_Current = new;
|
|
|
|
return old;
|
|
}
|
|
|
|
/* An extension mechanism to store arbitrary additional per-thread state.
|
|
PyThreadState_GetDict() returns a dictionary that can be used to hold such
|
|
state; the caller should pick a unique key and store its state there. If
|
|
PyThreadState_GetDict() returns NULL, an exception has been raised (most
|
|
likely MemoryError) and the caller should pass on the exception. */
|
|
|
|
PyObject *
|
|
PyThreadState_GetDict(void)
|
|
{
|
|
if (_PyThreadState_Current == NULL)
|
|
Py_FatalError("PyThreadState_GetDict: no current thread");
|
|
|
|
if (_PyThreadState_Current->dict == NULL)
|
|
_PyThreadState_Current->dict = PyDict_New();
|
|
return _PyThreadState_Current->dict;
|
|
}
|