bpo-33608: Minor cleanup related to pending calls. (gh-12247)
This commit is contained in:
parent
7bda9de550
commit
5be45a6105
|
@ -12,7 +12,6 @@ extern "C" {
|
||||||
#include "pythread.h"
|
#include "pythread.h"
|
||||||
|
|
||||||
struct _pending_calls {
|
struct _pending_calls {
|
||||||
unsigned long main_thread;
|
|
||||||
PyThread_type_lock lock;
|
PyThread_type_lock lock;
|
||||||
/* Request for running pending calls. */
|
/* Request for running pending calls. */
|
||||||
_Py_atomic_int calls_to_do;
|
_Py_atomic_int calls_to_do;
|
||||||
|
|
|
@ -31,6 +31,8 @@ struct _is {
|
||||||
int64_t id_refcount;
|
int64_t id_refcount;
|
||||||
PyThread_type_lock id_mutex;
|
PyThread_type_lock id_mutex;
|
||||||
|
|
||||||
|
int finalizing;
|
||||||
|
|
||||||
PyObject *modules;
|
PyObject *modules;
|
||||||
PyObject *modules_by_index;
|
PyObject *modules_by_index;
|
||||||
PyObject *sysdict;
|
PyObject *sysdict;
|
||||||
|
@ -207,6 +209,8 @@ typedef struct pyruntimestate {
|
||||||
struct _xidregitem *head;
|
struct _xidregitem *head;
|
||||||
} xidregistry;
|
} xidregistry;
|
||||||
|
|
||||||
|
unsigned long main_thread;
|
||||||
|
|
||||||
#define NEXITFUNCS 32
|
#define NEXITFUNCS 32
|
||||||
void (*exitfuncs[NEXITFUNCS])(void);
|
void (*exitfuncs[NEXITFUNCS])(void);
|
||||||
int nexitfuncs;
|
int nexitfuncs;
|
||||||
|
|
|
@ -174,9 +174,11 @@ PyEval_InitThreads(void)
|
||||||
PyThread_init_thread();
|
PyThread_init_thread();
|
||||||
create_gil();
|
create_gil();
|
||||||
take_gil(_PyThreadState_GET());
|
take_gil(_PyThreadState_GET());
|
||||||
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
|
// Set it to the ID of the main thread of the main interpreter.
|
||||||
if (!_PyRuntime.ceval.pending.lock)
|
_PyRuntime.main_thread = PyThread_get_thread_ident();
|
||||||
|
if (!_PyRuntime.ceval.pending.lock) {
|
||||||
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
|
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -243,9 +245,9 @@ PyEval_ReInitThreads(void)
|
||||||
if (!gil_created())
|
if (!gil_created())
|
||||||
return;
|
return;
|
||||||
recreate_gil();
|
recreate_gil();
|
||||||
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
|
|
||||||
take_gil(current_tstate);
|
take_gil(current_tstate);
|
||||||
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
|
_PyRuntime.main_thread = PyThread_get_thread_ident();
|
||||||
|
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
|
||||||
|
|
||||||
/* Destroy all threads except the current one */
|
/* Destroy all threads except the current one */
|
||||||
_PyThreadState_DeleteExcept(current_tstate);
|
_PyThreadState_DeleteExcept(current_tstate);
|
||||||
|
@ -323,6 +325,35 @@ _PyEval_SignalReceived(void)
|
||||||
SIGNAL_PENDING_SIGNALS();
|
SIGNAL_PENDING_SIGNALS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Push one item onto the queue while holding the lock. */
|
||||||
|
static int
|
||||||
|
_push_pending_call(int (*func)(void *), void *arg)
|
||||||
|
{
|
||||||
|
int i = _PyRuntime.ceval.pending.last;
|
||||||
|
int j = (i + 1) % NPENDINGCALLS;
|
||||||
|
if (j == _PyRuntime.ceval.pending.first) {
|
||||||
|
return -1; /* Queue full */
|
||||||
|
}
|
||||||
|
_PyRuntime.ceval.pending.calls[i].func = func;
|
||||||
|
_PyRuntime.ceval.pending.calls[i].arg = arg;
|
||||||
|
_PyRuntime.ceval.pending.last = j;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pop one item off the queue while holding the lock. */
|
||||||
|
static void
|
||||||
|
_pop_pending_call(int (**func)(void *), void **arg)
|
||||||
|
{
|
||||||
|
int i = _PyRuntime.ceval.pending.first;
|
||||||
|
if (i == _PyRuntime.ceval.pending.last) {
|
||||||
|
return; /* Queue empty */
|
||||||
|
}
|
||||||
|
|
||||||
|
*func = _PyRuntime.ceval.pending.calls[i].func;
|
||||||
|
*arg = _PyRuntime.ceval.pending.calls[i].arg;
|
||||||
|
_PyRuntime.ceval.pending.first = (i + 1) % NPENDINGCALLS;
|
||||||
|
}
|
||||||
|
|
||||||
/* This implementation is thread-safe. It allows
|
/* This implementation is thread-safe. It allows
|
||||||
scheduling to be made from any thread, and even from an executing
|
scheduling to be made from any thread, and even from an executing
|
||||||
callback.
|
callback.
|
||||||
|
@ -331,7 +362,6 @@ _PyEval_SignalReceived(void)
|
||||||
int
|
int
|
||||||
Py_AddPendingCall(int (*func)(void *), void *arg)
|
Py_AddPendingCall(int (*func)(void *), void *arg)
|
||||||
{
|
{
|
||||||
int i, j, result=0;
|
|
||||||
PyThread_type_lock lock = _PyRuntime.ceval.pending.lock;
|
PyThread_type_lock lock = _PyRuntime.ceval.pending.lock;
|
||||||
|
|
||||||
/* try a few times for the lock. Since this mechanism is used
|
/* try a few times for the lock. Since this mechanism is used
|
||||||
|
@ -346,6 +376,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
|
||||||
* this function is called before any bytecode evaluation takes place.
|
* this function is called before any bytecode evaluation takes place.
|
||||||
*/
|
*/
|
||||||
if (lock != NULL) {
|
if (lock != NULL) {
|
||||||
|
int i;
|
||||||
for (i = 0; i<100; i++) {
|
for (i = 0; i<100; i++) {
|
||||||
if (PyThread_acquire_lock(lock, NOWAIT_LOCK))
|
if (PyThread_acquire_lock(lock, NOWAIT_LOCK))
|
||||||
break;
|
break;
|
||||||
|
@ -354,15 +385,8 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = _PyRuntime.ceval.pending.last;
|
int result = _push_pending_call(func, arg);
|
||||||
j = (i + 1) % NPENDINGCALLS;
|
|
||||||
if (j == _PyRuntime.ceval.pending.first) {
|
|
||||||
result = -1; /* Queue full */
|
|
||||||
} else {
|
|
||||||
_PyRuntime.ceval.pending.calls[i].func = func;
|
|
||||||
_PyRuntime.ceval.pending.calls[i].arg = arg;
|
|
||||||
_PyRuntime.ceval.pending.last = j;
|
|
||||||
}
|
|
||||||
/* signal main loop */
|
/* signal main loop */
|
||||||
SIGNAL_PENDING_CALLS();
|
SIGNAL_PENDING_CALLS();
|
||||||
if (lock != NULL)
|
if (lock != NULL)
|
||||||
|
@ -373,10 +397,10 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
|
||||||
static int
|
static int
|
||||||
handle_signals(void)
|
handle_signals(void)
|
||||||
{
|
{
|
||||||
/* Only handle signals on main thread. */
|
/* Only handle signals on main thread. PyEval_InitThreads must
|
||||||
if (_PyRuntime.ceval.pending.main_thread &&
|
* have been called already.
|
||||||
PyThread_get_thread_ident() != _PyRuntime.ceval.pending.main_thread)
|
*/
|
||||||
{
|
if (PyThread_get_thread_ident() != _PyRuntime.main_thread) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -401,9 +425,7 @@ make_pending_calls(void)
|
||||||
static int busy = 0;
|
static int busy = 0;
|
||||||
|
|
||||||
/* only service pending calls on main thread */
|
/* only service pending calls on main thread */
|
||||||
if (_PyRuntime.ceval.pending.main_thread &&
|
if (PyThread_get_thread_ident() != _PyRuntime.main_thread) {
|
||||||
PyThread_get_thread_ident() != _PyRuntime.ceval.pending.main_thread)
|
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,24 +450,18 @@ make_pending_calls(void)
|
||||||
|
|
||||||
/* perform a bounded number of calls, in case of recursion */
|
/* perform a bounded number of calls, in case of recursion */
|
||||||
for (int i=0; i<NPENDINGCALLS; i++) {
|
for (int i=0; i<NPENDINGCALLS; i++) {
|
||||||
int j;
|
int (*func)(void *) = NULL;
|
||||||
int (*func)(void *);
|
|
||||||
void *arg = NULL;
|
void *arg = NULL;
|
||||||
|
|
||||||
/* pop one item off the queue while holding the lock */
|
/* pop one item off the queue while holding the lock */
|
||||||
PyThread_acquire_lock(_PyRuntime.ceval.pending.lock, WAIT_LOCK);
|
PyThread_acquire_lock(_PyRuntime.ceval.pending.lock, WAIT_LOCK);
|
||||||
j = _PyRuntime.ceval.pending.first;
|
_pop_pending_call(&func, &arg);
|
||||||
if (j == _PyRuntime.ceval.pending.last) {
|
|
||||||
func = NULL; /* Queue empty */
|
|
||||||
} else {
|
|
||||||
func = _PyRuntime.ceval.pending.calls[j].func;
|
|
||||||
arg = _PyRuntime.ceval.pending.calls[j].arg;
|
|
||||||
_PyRuntime.ceval.pending.first = (j + 1) % NPENDINGCALLS;
|
|
||||||
}
|
|
||||||
PyThread_release_lock(_PyRuntime.ceval.pending.lock);
|
PyThread_release_lock(_PyRuntime.ceval.pending.lock);
|
||||||
|
|
||||||
/* having released the lock, perform the callback */
|
/* having released the lock, perform the callback */
|
||||||
if (func == NULL)
|
if (func == NULL) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
res = func(arg);
|
res = func(arg);
|
||||||
if (res) {
|
if (res) {
|
||||||
goto error;
|
goto error;
|
||||||
|
|
|
@ -1460,6 +1460,7 @@ Py_EndInterpreter(PyThreadState *tstate)
|
||||||
Py_FatalError("Py_EndInterpreter: thread is not current");
|
Py_FatalError("Py_EndInterpreter: thread is not current");
|
||||||
if (tstate->frame != NULL)
|
if (tstate->frame != NULL)
|
||||||
Py_FatalError("Py_EndInterpreter: thread still has a frame");
|
Py_FatalError("Py_EndInterpreter: thread still has a frame");
|
||||||
|
interp->finalizing = 1;
|
||||||
|
|
||||||
wait_for_thread_shutdown();
|
wait_for_thread_shutdown();
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,8 @@ _PyRuntimeState_Init_impl(_PyRuntimeState *runtime)
|
||||||
return _Py_INIT_ERR("Can't initialize threads for cross-interpreter data registry");
|
return _Py_INIT_ERR("Can't initialize threads for cross-interpreter data registry");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runtime->main_thread is set in PyEval_InitThreads().
|
||||||
|
|
||||||
return _Py_INIT_OK();
|
return _Py_INIT_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,28 +135,12 @@ PyInterpreterState_New(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(interp, 0, sizeof(*interp));
|
||||||
interp->id_refcount = -1;
|
interp->id_refcount = -1;
|
||||||
interp->id_mutex = NULL;
|
|
||||||
interp->modules = NULL;
|
|
||||||
interp->modules_by_index = NULL;
|
|
||||||
interp->sysdict = NULL;
|
|
||||||
interp->builtins = NULL;
|
|
||||||
interp->builtins_copy = NULL;
|
|
||||||
interp->tstate_head = NULL;
|
|
||||||
interp->check_interval = 100;
|
interp->check_interval = 100;
|
||||||
interp->num_threads = 0;
|
|
||||||
interp->pythread_stacksize = 0;
|
|
||||||
interp->codec_search_path = NULL;
|
|
||||||
interp->codec_search_cache = NULL;
|
|
||||||
interp->codec_error_registry = NULL;
|
|
||||||
interp->codecs_initialized = 0;
|
|
||||||
interp->fscodec_initialized = 0;
|
|
||||||
interp->core_config = _PyCoreConfig_INIT;
|
interp->core_config = _PyCoreConfig_INIT;
|
||||||
interp->config = _PyMainInterpreterConfig_INIT;
|
interp->config = _PyMainInterpreterConfig_INIT;
|
||||||
interp->importlib = NULL;
|
|
||||||
interp->import_func = NULL;
|
|
||||||
interp->eval_frame = _PyEval_EvalFrameDefault;
|
interp->eval_frame = _PyEval_EvalFrameDefault;
|
||||||
interp->co_extra_user_count = 0;
|
|
||||||
#ifdef HAVE_DLOPEN
|
#ifdef HAVE_DLOPEN
|
||||||
#if HAVE_DECL_RTLD_NOW
|
#if HAVE_DECL_RTLD_NOW
|
||||||
interp->dlopenflags = RTLD_NOW;
|
interp->dlopenflags = RTLD_NOW;
|
||||||
|
@ -162,13 +148,6 @@ PyInterpreterState_New(void)
|
||||||
interp->dlopenflags = RTLD_LAZY;
|
interp->dlopenflags = RTLD_LAZY;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_FORK
|
|
||||||
interp->before_forkers = NULL;
|
|
||||||
interp->after_forkers_parent = NULL;
|
|
||||||
interp->after_forkers_child = NULL;
|
|
||||||
#endif
|
|
||||||
interp->pyexitfunc = NULL;
|
|
||||||
interp->pyexitmodule = NULL;
|
|
||||||
|
|
||||||
HEAD_LOCK();
|
HEAD_LOCK();
|
||||||
if (_PyRuntime.interpreters.next_id < 0) {
|
if (_PyRuntime.interpreters.next_id < 0) {
|
||||||
|
@ -223,6 +202,9 @@ PyInterpreterState_Clear(PyInterpreterState *interp)
|
||||||
Py_CLEAR(interp->after_forkers_parent);
|
Py_CLEAR(interp->after_forkers_parent);
|
||||||
Py_CLEAR(interp->after_forkers_child);
|
Py_CLEAR(interp->after_forkers_child);
|
||||||
#endif
|
#endif
|
||||||
|
// XXX Once we have one allocator per interpreter (i.e.
|
||||||
|
// per-interpreter GC) we must ensure that all of the interpreter's
|
||||||
|
// objects have been cleaned up at the point.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -334,28 +316,39 @@ PyInterpreterState_GetID(PyInterpreterState *interp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PyInterpreterState *
|
static PyInterpreterState *
|
||||||
_PyInterpreterState_LookUpID(PY_INT64_T requested_id)
|
interp_look_up_id(PY_INT64_T requested_id)
|
||||||
{
|
{
|
||||||
if (requested_id < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
PyInterpreterState *interp = PyInterpreterState_Head();
|
PyInterpreterState *interp = PyInterpreterState_Head();
|
||||||
while (interp != NULL) {
|
while (interp != NULL) {
|
||||||
PY_INT64_T id = PyInterpreterState_GetID(interp);
|
PY_INT64_T id = PyInterpreterState_GetID(interp);
|
||||||
if (id < 0)
|
if (id < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
if (requested_id == id)
|
}
|
||||||
|
if (requested_id == id) {
|
||||||
return interp;
|
return interp;
|
||||||
|
}
|
||||||
interp = PyInterpreterState_Next(interp);
|
interp = PyInterpreterState_Next(interp);
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
|
||||||
PyErr_Format(PyExc_RuntimeError,
|
|
||||||
"unrecognized interpreter ID %lld", requested_id);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyInterpreterState *
|
||||||
|
_PyInterpreterState_LookUpID(PY_INT64_T requested_id)
|
||||||
|
{
|
||||||
|
PyInterpreterState *interp = NULL;
|
||||||
|
if (requested_id >= 0) {
|
||||||
|
HEAD_LOCK();
|
||||||
|
interp = interp_look_up_id(requested_id);
|
||||||
|
HEAD_UNLOCK();
|
||||||
|
}
|
||||||
|
if (interp == NULL && !PyErr_Occurred()) {
|
||||||
|
PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"unrecognized interpreter ID %lld", requested_id);
|
||||||
|
}
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyInterpreterState_IDInitref(PyInterpreterState *interp)
|
_PyInterpreterState_IDInitref(PyInterpreterState *interp)
|
||||||
|
|
Loading…
Reference in New Issue