bpo-33608: Minor cleanup related to pending calls. (gh-12247)

This commit is contained in:
Eric Snow 2019-03-08 22:47:07 -07:00 committed by GitHub
parent 7bda9de550
commit 5be45a6105
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 68 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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)