mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	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
					
				
					 2 changed files with 53 additions and 66 deletions
				
			
		| 
						 | 
					@ -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 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                /* 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue