bpo-39877: Refactor take_gil() function (GH-18885)
* Remove ceval parameter of take_gil(): get it from tstate. * Move exit_thread_if_finalizing() call inside take_gil(). Replace exit_thread_if_finalizing() with tstate_must_exit(): the caller is now responsible to call PyThread_exit_thread(). * Move is_tstate_valid() assertion inside take_gil(). Remove is_tstate_valid(): inline code into take_gil(). * Move gil_created() assertion inside take_gil().
This commit is contained in:
parent
363fab83b8
commit
85f5a69ae1
|
@ -198,16 +198,6 @@ ensure_tstate_not_null(const char *func, PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
static int is_tstate_valid(PyThreadState *tstate)
|
|
||||||
{
|
|
||||||
assert(!_PyMem_IsPtrFreed(tstate));
|
|
||||||
assert(!_PyMem_IsPtrFreed(tstate->interp));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
PyEval_ThreadsInitialized(void)
|
PyEval_ThreadsInitialized(void)
|
||||||
{
|
{
|
||||||
|
@ -230,13 +220,15 @@ _PyEval_InitThreads(PyThreadState *tstate)
|
||||||
|
|
||||||
PyThread_init_thread();
|
PyThread_init_thread();
|
||||||
create_gil(gil);
|
create_gil(gil);
|
||||||
take_gil(ceval, tstate);
|
|
||||||
|
take_gil(tstate);
|
||||||
|
|
||||||
struct _pending_calls *pending = &ceval->pending;
|
struct _pending_calls *pending = &ceval->pending;
|
||||||
pending->lock = PyThread_allocate_lock();
|
pending->lock = PyThread_allocate_lock();
|
||||||
if (pending->lock == NULL) {
|
if (pending->lock == NULL) {
|
||||||
return _PyStatus_NO_MEMORY();
|
return _PyStatus_NO_MEMORY();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _PyStatus_OK();
|
return _PyStatus_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,30 +261,6 @@ _PyEval_FiniThreads(struct _ceval_runtime_state *ceval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is designed to exit daemon threads immediately rather than
|
|
||||||
taking the GIL if Py_Finalize() has been called.
|
|
||||||
|
|
||||||
The caller must *not* hold the GIL, since this function does not release
|
|
||||||
the GIL before exiting the thread.
|
|
||||||
|
|
||||||
When this function is called by a daemon thread after Py_Finalize() has been
|
|
||||||
called, the GIL does no longer exist.
|
|
||||||
|
|
||||||
tstate must be non-NULL. */
|
|
||||||
static inline void
|
|
||||||
exit_thread_if_finalizing(PyThreadState *tstate)
|
|
||||||
{
|
|
||||||
/* bpo-39877: Access _PyRuntime directly rather than using
|
|
||||||
tstate->interp->runtime to support calls from Python daemon threads.
|
|
||||||
After Py_Finalize() has been called, tstate can be a dangling pointer:
|
|
||||||
point to PyThreadState freed memory. */
|
|
||||||
_PyRuntimeState *runtime = &_PyRuntime;
|
|
||||||
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
|
|
||||||
if (finalizing != NULL && finalizing != tstate) {
|
|
||||||
PyThread_exit_thread();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyEval_Fini(void)
|
_PyEval_Fini(void)
|
||||||
{
|
{
|
||||||
|
@ -329,11 +297,7 @@ PyEval_AcquireLock(void)
|
||||||
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
|
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
|
||||||
ensure_tstate_not_null(__func__, tstate);
|
ensure_tstate_not_null(__func__, tstate);
|
||||||
|
|
||||||
exit_thread_if_finalizing(tstate);
|
take_gil(tstate);
|
||||||
assert(is_tstate_valid(tstate));
|
|
||||||
|
|
||||||
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
|
||||||
take_gil(ceval, tstate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -353,17 +317,10 @@ PyEval_AcquireThread(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
ensure_tstate_not_null(__func__, tstate);
|
ensure_tstate_not_null(__func__, tstate);
|
||||||
|
|
||||||
exit_thread_if_finalizing(tstate);
|
take_gil(tstate);
|
||||||
assert(is_tstate_valid(tstate));
|
|
||||||
|
|
||||||
_PyRuntimeState *runtime = tstate->interp->runtime;
|
struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate;
|
||||||
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
if (_PyThreadState_Swap(gilstate, tstate) != NULL) {
|
||||||
|
|
||||||
/* Check that _PyEval_InitThreads() was called to create the lock */
|
|
||||||
assert(gil_created(&ceval->gil));
|
|
||||||
|
|
||||||
take_gil(ceval, tstate);
|
|
||||||
if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
|
|
||||||
Py_FatalError("non-NULL old thread state");
|
Py_FatalError("non-NULL old thread state");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,7 +353,8 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
|
||||||
recreate_gil(&ceval->gil);
|
recreate_gil(&ceval->gil);
|
||||||
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
|
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
|
||||||
ensure_tstate_not_null(__func__, tstate);
|
ensure_tstate_not_null(__func__, tstate);
|
||||||
take_gil(ceval, tstate);
|
|
||||||
|
take_gil(tstate);
|
||||||
|
|
||||||
struct _pending_calls *pending = &ceval->pending;
|
struct _pending_calls *pending = &ceval->pending;
|
||||||
pending->lock = PyThread_allocate_lock();
|
pending->lock = PyThread_allocate_lock();
|
||||||
|
@ -436,16 +394,10 @@ PyEval_RestoreThread(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
ensure_tstate_not_null(__func__, tstate);
|
ensure_tstate_not_null(__func__, tstate);
|
||||||
|
|
||||||
exit_thread_if_finalizing(tstate);
|
take_gil(tstate);
|
||||||
assert(is_tstate_valid(tstate));
|
|
||||||
|
|
||||||
_PyRuntimeState *runtime = tstate->interp->runtime;
|
struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate;
|
||||||
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
_PyThreadState_Swap(gilstate, tstate);
|
||||||
assert(gil_created(&ceval->gil));
|
|
||||||
|
|
||||||
take_gil(ceval, tstate);
|
|
||||||
|
|
||||||
_PyThreadState_Swap(&runtime->gilstate, tstate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -805,7 +757,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
|
|
||||||
PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime);
|
PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime);
|
||||||
ensure_tstate_not_null(__func__, tstate);
|
ensure_tstate_not_null(__func__, tstate);
|
||||||
assert(is_tstate_valid(tstate));
|
|
||||||
|
|
||||||
/* when tracing we set things up so that
|
/* when tracing we set things up so that
|
||||||
|
|
||||||
|
@ -1294,10 +1245,7 @@ main_loop:
|
||||||
|
|
||||||
/* Other threads may run now */
|
/* Other threads may run now */
|
||||||
|
|
||||||
/* Check if we should make a quick exit. */
|
take_gil(tstate);
|
||||||
exit_thread_if_finalizing(tstate);
|
|
||||||
|
|
||||||
take_gil(ceval, tstate);
|
|
||||||
|
|
||||||
if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
|
if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
|
||||||
Py_FatalError("orphan tstate");
|
Py_FatalError("orphan tstate");
|
||||||
|
|
|
@ -180,17 +180,55 @@ drop_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check if a Python thread must exit immediately, rather than taking the GIL
|
||||||
|
if Py_Finalize() has been called.
|
||||||
|
|
||||||
|
When this function is called by a daemon thread after Py_Finalize() has been
|
||||||
|
called, the GIL does no longer exist.
|
||||||
|
|
||||||
|
tstate must be non-NULL. */
|
||||||
|
static inline int
|
||||||
|
tstate_must_exit(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
/* bpo-39877: Access _PyRuntime directly rather than using
|
||||||
|
tstate->interp->runtime to support calls from Python daemon threads.
|
||||||
|
After Py_Finalize() has been called, tstate can be a dangling pointer:
|
||||||
|
point to PyThreadState freed memory. */
|
||||||
|
_PyRuntimeState *runtime = &_PyRuntime;
|
||||||
|
PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
|
||||||
|
return (finalizing != NULL && finalizing != tstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Take the GIL.
|
/* Take the GIL.
|
||||||
|
|
||||||
The function saves errno at entry and restores its value at exit.
|
The function saves errno at entry and restores its value at exit.
|
||||||
|
|
||||||
tstate must be non-NULL. */
|
tstate must be non-NULL. */
|
||||||
static void
|
static void
|
||||||
take_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate)
|
take_gil(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
int err = errno;
|
int err = errno;
|
||||||
|
|
||||||
|
assert(tstate != NULL);
|
||||||
|
|
||||||
|
/* Check if we should make a quick exit. */
|
||||||
|
if (tstate_must_exit(tstate)) {
|
||||||
|
PyThread_exit_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure that tstate is valid: sanity check for PyEval_AcquireThread() and
|
||||||
|
PyEval_RestoreThread(). Detect if tstate memory was freed. */
|
||||||
|
assert(!_PyMem_IsPtrFreed(tstate));
|
||||||
|
assert(!_PyMem_IsPtrFreed(tstate->interp));
|
||||||
|
|
||||||
|
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
struct _gil_runtime_state *gil = &ceval->gil;
|
struct _gil_runtime_state *gil = &ceval->gil;
|
||||||
|
|
||||||
|
/* Check that _PyEval_InitThreads() was called to create the lock */
|
||||||
|
assert(gil_created(gil));
|
||||||
|
|
||||||
MUTEX_LOCK(gil->mutex);
|
MUTEX_LOCK(gil->mutex);
|
||||||
|
|
||||||
if (!_Py_atomic_load_relaxed(&gil->locked)) {
|
if (!_Py_atomic_load_relaxed(&gil->locked)) {
|
||||||
|
@ -215,6 +253,7 @@ take_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate)
|
||||||
SET_GIL_DROP_REQUEST(ceval);
|
SET_GIL_DROP_REQUEST(ceval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ready:
|
_ready:
|
||||||
#ifdef FORCE_SWITCHING
|
#ifdef FORCE_SWITCHING
|
||||||
/* This mutex must be taken before modifying gil->last_holder:
|
/* This mutex must be taken before modifying gil->last_holder:
|
||||||
|
|
Loading…
Reference in New Issue