bpo-40010: Pass tstate to ceval GIL functions (GH-19077)
* Add eval_frame_handle_pending() helper function: cold code path. * Fix PyEval_ReleaseLock(): don't dereference tstate if it's NULL.
This commit is contained in:
parent
8334f30a74
commit
da2914db4b
312
Python/ceval.c
312
Python/ceval.c
|
@ -121,6 +121,21 @@ static size_t opcache_global_misses = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
/* Ensure that tstate is valid: sanity check for PyEval_AcquireThread() and
|
||||||
|
PyEval_RestoreThread(). Detect if tstate memory was freed. It can happen
|
||||||
|
when a thread continues to run after Python finalization, especially
|
||||||
|
daemon threads. */
|
||||||
|
static int
|
||||||
|
is_tstate_valid(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
assert(!_PyMem_IsPtrFreed(tstate));
|
||||||
|
assert(!_PyMem_IsPtrFreed(tstate->interp));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Only handle signals on the main thread of the main interpreter. */
|
/* Only handle signals on the main thread of the main interpreter. */
|
||||||
static int
|
static int
|
||||||
thread_can_handle_signals(void)
|
thread_can_handle_signals(void)
|
||||||
|
@ -132,64 +147,106 @@ thread_can_handle_signals(void)
|
||||||
/* This can set eval_breaker to 0 even though gil_drop_request became
|
/* This can set eval_breaker to 0 even though gil_drop_request became
|
||||||
1. We believe this is all right because the eval loop will release
|
1. We believe this is all right because the eval loop will release
|
||||||
the GIL eventually anyway. */
|
the GIL eventually anyway. */
|
||||||
#define COMPUTE_EVAL_BREAKER(ceval, ceval2) \
|
static inline void
|
||||||
_Py_atomic_store_relaxed( \
|
COMPUTE_EVAL_BREAKER(PyThreadState *tstate,
|
||||||
&(ceval2)->eval_breaker, \
|
struct _ceval_runtime_state *ceval,
|
||||||
_Py_atomic_load_relaxed(&(ceval)->gil_drop_request) | \
|
struct _ceval_state *ceval2)
|
||||||
(_Py_atomic_load_relaxed(&(ceval)->signals_pending) \
|
{
|
||||||
&& thread_can_handle_signals()) | \
|
assert(is_tstate_valid(tstate));
|
||||||
_Py_atomic_load_relaxed(&(ceval2)->pending.calls_to_do) | \
|
_Py_atomic_store_relaxed(&ceval2->eval_breaker,
|
||||||
(ceval2)->pending.async_exc)
|
_Py_atomic_load_relaxed(&ceval->gil_drop_request)
|
||||||
|
| (_Py_atomic_load_relaxed(&ceval->signals_pending)
|
||||||
|
&& thread_can_handle_signals())
|
||||||
|
| _Py_atomic_load_relaxed(&ceval2->pending.calls_to_do)
|
||||||
|
| ceval2->pending.async_exc);
|
||||||
|
}
|
||||||
|
|
||||||
#define SET_GIL_DROP_REQUEST(ceval) \
|
|
||||||
do { \
|
|
||||||
_Py_atomic_store_relaxed(&(ceval)->gil_drop_request, 1); \
|
|
||||||
_Py_atomic_store_relaxed(&(ceval2)->eval_breaker, 1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define RESET_GIL_DROP_REQUEST(ceval, ceval2) \
|
static inline void
|
||||||
do { \
|
SET_GIL_DROP_REQUEST(PyThreadState *tstate)
|
||||||
_Py_atomic_store_relaxed(&(ceval)->gil_drop_request, 0); \
|
{
|
||||||
COMPUTE_EVAL_BREAKER(ceval, ceval2); \
|
assert(is_tstate_valid(tstate));
|
||||||
} while (0)
|
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
_Py_atomic_store_relaxed(&ceval->gil_drop_request, 1);
|
||||||
|
_Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Pending calls are only modified under pending_lock */
|
|
||||||
#define SIGNAL_PENDING_CALLS(ceval2) \
|
|
||||||
do { \
|
|
||||||
_Py_atomic_store_relaxed(&(ceval2)->pending.calls_to_do, 1); \
|
|
||||||
_Py_atomic_store_relaxed(&(ceval2)->eval_breaker, 1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define UNSIGNAL_PENDING_CALLS(ceval, ceval2) \
|
static inline void
|
||||||
do { \
|
RESET_GIL_DROP_REQUEST(PyThreadState *tstate)
|
||||||
_Py_atomic_store_relaxed(&(ceval2)->pending.calls_to_do, 0); \
|
{
|
||||||
COMPUTE_EVAL_BREAKER(ceval, ceval2); \
|
assert(is_tstate_valid(tstate));
|
||||||
} while (0)
|
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
_Py_atomic_store_relaxed(&ceval->gil_drop_request, 0);
|
||||||
|
COMPUTE_EVAL_BREAKER(tstate, ceval, ceval2);
|
||||||
|
}
|
||||||
|
|
||||||
/* eval_breaker is not set to 1 if thread_can_handle_signals() is false. */
|
|
||||||
#define SIGNAL_PENDING_SIGNALS(ceval, ceval2) \
|
|
||||||
do { \
|
|
||||||
_Py_atomic_store_relaxed(&(ceval)->signals_pending, 1); \
|
|
||||||
COMPUTE_EVAL_BREAKER(ceval, ceval2); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define UNSIGNAL_PENDING_SIGNALS(ceval, ceval2) \
|
static inline void
|
||||||
do { \
|
SIGNAL_PENDING_CALLS(PyThreadState *tstate)
|
||||||
_Py_atomic_store_relaxed(&(ceval)->signals_pending, 0); \
|
{
|
||||||
COMPUTE_EVAL_BREAKER(ceval, ceval2); \
|
assert(is_tstate_valid(tstate));
|
||||||
} while (0)
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
_Py_atomic_store_relaxed(&ceval2->pending.calls_to_do, 1);
|
||||||
|
_Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#define SIGNAL_ASYNC_EXC(ceval2) \
|
|
||||||
do { \
|
|
||||||
(ceval2)->pending.async_exc = 1; \
|
|
||||||
_Py_atomic_store_relaxed(&(ceval2)->eval_breaker, 1); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define UNSIGNAL_ASYNC_EXC(ceval, ceval2) \
|
static inline void
|
||||||
do { \
|
UNSIGNAL_PENDING_CALLS(PyThreadState *tstate)
|
||||||
(ceval2)->pending.async_exc = 0; \
|
{
|
||||||
COMPUTE_EVAL_BREAKER(ceval, ceval2); \
|
assert(is_tstate_valid(tstate));
|
||||||
} while (0)
|
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
_Py_atomic_store_relaxed(&ceval2->pending.calls_to_do, 0);
|
||||||
|
COMPUTE_EVAL_BREAKER(tstate, ceval, ceval2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
SIGNAL_PENDING_SIGNALS(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
assert(is_tstate_valid(tstate));
|
||||||
|
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
_Py_atomic_store_relaxed(&ceval->signals_pending, 1);
|
||||||
|
/* eval_breaker is not set to 1 if thread_can_handle_signals() is false */
|
||||||
|
COMPUTE_EVAL_BREAKER(tstate, ceval, ceval2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
UNSIGNAL_PENDING_SIGNALS(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
assert(is_tstate_valid(tstate));
|
||||||
|
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
_Py_atomic_store_relaxed(&ceval->signals_pending, 0);
|
||||||
|
COMPUTE_EVAL_BREAKER(tstate, ceval, ceval2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
SIGNAL_ASYNC_EXC(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
assert(is_tstate_valid(tstate));
|
||||||
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
ceval2->pending.async_exc = 1;
|
||||||
|
_Py_atomic_store_relaxed(&ceval2->eval_breaker, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
UNSIGNAL_ASYNC_EXC(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
assert(is_tstate_valid(tstate));
|
||||||
|
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
ceval2->pending.async_exc = 0;
|
||||||
|
COMPUTE_EVAL_BREAKER(tstate, ceval, ceval2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_ERRNO_H
|
#ifdef HAVE_ERRNO_H
|
||||||
|
@ -224,7 +281,8 @@ PyEval_ThreadsInitialized(void)
|
||||||
PyStatus
|
PyStatus
|
||||||
_PyEval_InitThreads(PyThreadState *tstate)
|
_PyEval_InitThreads(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
assert(tstate != NULL);
|
assert(is_tstate_valid(tstate));
|
||||||
|
|
||||||
if (_Py_IsMainInterpreter(tstate)) {
|
if (_Py_IsMainInterpreter(tstate)) {
|
||||||
struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
|
struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
|
||||||
if (gil_created(gil)) {
|
if (gil_created(gil)) {
|
||||||
|
@ -315,20 +373,17 @@ PyEval_ReleaseLock(void)
|
||||||
{
|
{
|
||||||
_PyRuntimeState *runtime = &_PyRuntime;
|
_PyRuntimeState *runtime = &_PyRuntime;
|
||||||
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
|
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
|
||||||
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
|
||||||
/* This function must succeed when the current thread state is NULL.
|
/* This function must succeed when the current thread state is NULL.
|
||||||
We therefore avoid PyThreadState_Get() which dumps a fatal error
|
We therefore avoid PyThreadState_Get() which dumps a fatal error
|
||||||
in debug mode.
|
in debug mode. */
|
||||||
*/
|
drop_gil(&runtime->ceval, tstate);
|
||||||
drop_gil(&runtime->ceval, ceval2, tstate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyEval_ReleaseLock(PyThreadState *tstate)
|
_PyEval_ReleaseLock(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
drop_gil(ceval, tstate);
|
||||||
drop_gil(ceval, ceval2, tstate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -347,15 +402,14 @@ PyEval_AcquireThread(PyThreadState *tstate)
|
||||||
void
|
void
|
||||||
PyEval_ReleaseThread(PyThreadState *tstate)
|
PyEval_ReleaseThread(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
assert(tstate != NULL);
|
assert(is_tstate_valid(tstate));
|
||||||
|
|
||||||
_PyRuntimeState *runtime = tstate->interp->runtime;
|
_PyRuntimeState *runtime = tstate->interp->runtime;
|
||||||
PyThreadState *new_tstate = _PyThreadState_Swap(&runtime->gilstate, NULL);
|
PyThreadState *new_tstate = _PyThreadState_Swap(&runtime->gilstate, NULL);
|
||||||
if (new_tstate != tstate) {
|
if (new_tstate != tstate) {
|
||||||
Py_FatalError("wrong thread state");
|
Py_FatalError("wrong thread state");
|
||||||
}
|
}
|
||||||
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
drop_gil(&runtime->ceval, tstate);
|
||||||
drop_gil(&runtime->ceval, ceval2, tstate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is called from PyOS_AfterFork_Child to destroy all threads
|
/* This function is called from PyOS_AfterFork_Child to destroy all threads
|
||||||
|
@ -392,8 +446,7 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
|
||||||
void
|
void
|
||||||
_PyEval_SignalAsyncExc(PyThreadState *tstate)
|
_PyEval_SignalAsyncExc(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
SIGNAL_ASYNC_EXC(tstate);
|
||||||
SIGNAL_ASYNC_EXC(ceval2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyThreadState *
|
PyThreadState *
|
||||||
|
@ -401,13 +454,12 @@ PyEval_SaveThread(void)
|
||||||
{
|
{
|
||||||
_PyRuntimeState *runtime = &_PyRuntime;
|
_PyRuntimeState *runtime = &_PyRuntime;
|
||||||
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
||||||
|
|
||||||
PyThreadState *tstate = _PyThreadState_Swap(&runtime->gilstate, NULL);
|
PyThreadState *tstate = _PyThreadState_Swap(&runtime->gilstate, NULL);
|
||||||
if (tstate == NULL) {
|
ensure_tstate_not_null(__func__, tstate);
|
||||||
Py_FatalError("NULL tstate");
|
|
||||||
}
|
|
||||||
assert(gil_created(&ceval->gil));
|
assert(gil_created(&ceval->gil));
|
||||||
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
drop_gil(ceval, tstate);
|
||||||
drop_gil(ceval, ceval2, tstate);
|
|
||||||
return tstate;
|
return tstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,12 +500,10 @@ PyEval_RestoreThread(PyThreadState *tstate)
|
||||||
void
|
void
|
||||||
_PyEval_SignalReceived(PyThreadState *tstate)
|
_PyEval_SignalReceived(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
|
||||||
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
|
||||||
/* bpo-30703: Function called when the C signal handler of Python gets a
|
/* bpo-30703: Function called when the C signal handler of Python gets a
|
||||||
signal. We cannot queue a callback using _PyEval_AddPendingCall() since
|
signal. We cannot queue a callback using _PyEval_AddPendingCall() since
|
||||||
that function is not async-signal-safe. */
|
that function is not async-signal-safe. */
|
||||||
SIGNAL_PENDING_SIGNALS(ceval, ceval2);
|
SIGNAL_PENDING_SIGNALS(tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push one item onto the queue while holding the lock. */
|
/* Push one item onto the queue while holding the lock. */
|
||||||
|
@ -496,8 +546,7 @@ int
|
||||||
_PyEval_AddPendingCall(PyThreadState *tstate,
|
_PyEval_AddPendingCall(PyThreadState *tstate,
|
||||||
int (*func)(void *), void *arg)
|
int (*func)(void *), void *arg)
|
||||||
{
|
{
|
||||||
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
struct _pending_calls *pending = &tstate->interp->ceval.pending;
|
||||||
struct _pending_calls *pending = &ceval2->pending;
|
|
||||||
|
|
||||||
PyThread_acquire_lock(pending->lock, WAIT_LOCK);
|
PyThread_acquire_lock(pending->lock, WAIT_LOCK);
|
||||||
if (pending->finishing) {
|
if (pending->finishing) {
|
||||||
|
@ -516,7 +565,7 @@ _PyEval_AddPendingCall(PyThreadState *tstate,
|
||||||
PyThread_release_lock(pending->lock);
|
PyThread_release_lock(pending->lock);
|
||||||
|
|
||||||
/* signal main loop */
|
/* signal main loop */
|
||||||
SIGNAL_PENDING_CALLS(ceval2);
|
SIGNAL_PENDING_CALLS(tstate);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,11 +612,10 @@ handle_signals(PyThreadState *tstate)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
UNSIGNAL_PENDING_SIGNALS(tstate);
|
||||||
struct _ceval_state *ceval2 = &interp->ceval;
|
|
||||||
UNSIGNAL_PENDING_SIGNALS(ceval, ceval2);
|
|
||||||
if (_PyErr_CheckSignals() < 0) {
|
if (_PyErr_CheckSignals() < 0) {
|
||||||
SIGNAL_PENDING_SIGNALS(ceval, ceval2); /* We're not done yet */
|
/* We're not done yet */
|
||||||
|
SIGNAL_PENDING_SIGNALS(tstate);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -576,10 +624,8 @@ handle_signals(PyThreadState *tstate)
|
||||||
static int
|
static int
|
||||||
make_pending_calls(PyThreadState *tstate)
|
make_pending_calls(PyThreadState *tstate)
|
||||||
{
|
{
|
||||||
static int busy = 0;
|
|
||||||
|
|
||||||
_PyRuntimeState *runtime = tstate->interp->runtime;
|
_PyRuntimeState *runtime = tstate->interp->runtime;
|
||||||
struct _ceval_state * ceval2 = &tstate->interp->ceval;
|
|
||||||
|
|
||||||
/* only service pending calls on main thread */
|
/* only service pending calls on main thread */
|
||||||
if (PyThread_get_thread_ident() != runtime->main_thread) {
|
if (PyThread_get_thread_ident() != runtime->main_thread) {
|
||||||
|
@ -587,18 +633,19 @@ make_pending_calls(PyThreadState *tstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* don't perform recursive pending calls */
|
/* don't perform recursive pending calls */
|
||||||
|
static int busy = 0;
|
||||||
if (busy) {
|
if (busy) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
busy = 1;
|
busy = 1;
|
||||||
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
|
||||||
/* unsignal before starting to call callbacks, so that any callback
|
/* unsignal before starting to call callbacks, so that any callback
|
||||||
added in-between re-signals */
|
added in-between re-signals */
|
||||||
UNSIGNAL_PENDING_CALLS(ceval, ceval2);
|
UNSIGNAL_PENDING_CALLS(tstate);
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
/* perform a bounded number of calls, in case of recursion */
|
/* perform a bounded number of calls, in case of recursion */
|
||||||
struct _pending_calls *pending = &ceval2->pending;
|
struct _pending_calls *pending = &tstate->interp->ceval.pending;
|
||||||
for (int i=0; i<NPENDINGCALLS; i++) {
|
for (int i=0; i<NPENDINGCALLS; i++) {
|
||||||
int (*func)(void *) = NULL;
|
int (*func)(void *) = NULL;
|
||||||
void *arg = NULL;
|
void *arg = NULL;
|
||||||
|
@ -623,7 +670,7 @@ make_pending_calls(PyThreadState *tstate)
|
||||||
|
|
||||||
error:
|
error:
|
||||||
busy = 0;
|
busy = 0;
|
||||||
SIGNAL_PENDING_CALLS(ceval2);
|
SIGNAL_PENDING_CALLS(tstate);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -789,6 +836,59 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
return _PyEval_EvalFrame(tstate, f, throwflag);
|
return _PyEval_EvalFrame(tstate, f, throwflag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Handle signals, pending calls, GIL drop request
|
||||||
|
and asynchronous exception */
|
||||||
|
static int
|
||||||
|
eval_frame_handle_pending(PyThreadState *tstate)
|
||||||
|
{
|
||||||
|
/* Pending signals */
|
||||||
|
_PyRuntimeState * const runtime = &_PyRuntime;
|
||||||
|
struct _ceval_runtime_state *ceval = &runtime->ceval;
|
||||||
|
if (_Py_atomic_load_relaxed(&ceval->signals_pending)) {
|
||||||
|
if (handle_signals(tstate) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pending calls */
|
||||||
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
if (_Py_atomic_load_relaxed(&ceval2->pending.calls_to_do)) {
|
||||||
|
if (make_pending_calls(tstate) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GIL drop request */
|
||||||
|
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
||||||
|
/* Give another thread a chance */
|
||||||
|
if (_PyThreadState_Swap(&runtime->gilstate, NULL) != tstate) {
|
||||||
|
Py_FatalError("tstate mix-up");
|
||||||
|
}
|
||||||
|
drop_gil(ceval, tstate);
|
||||||
|
|
||||||
|
/* Other threads may run now */
|
||||||
|
|
||||||
|
take_gil(tstate);
|
||||||
|
|
||||||
|
if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
|
||||||
|
Py_FatalError("orphan tstate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for asynchronous exception. */
|
||||||
|
if (tstate->async_exc != NULL) {
|
||||||
|
PyObject *exc = tstate->async_exc;
|
||||||
|
tstate->async_exc = NULL;
|
||||||
|
UNSIGNAL_ASYNC_EXC(tstate);
|
||||||
|
_PyErr_SetNone(tstate, exc);
|
||||||
|
Py_DECREF(exc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject* _Py_HOT_FUNCTION
|
PyObject* _Py_HOT_FUNCTION
|
||||||
_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
||||||
{
|
{
|
||||||
|
@ -803,8 +903,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
||||||
int oparg; /* Current opcode argument, if any */
|
int oparg; /* Current opcode argument, if any */
|
||||||
PyObject **fastlocals, **freevars;
|
PyObject **fastlocals, **freevars;
|
||||||
PyObject *retval = NULL; /* Return value */
|
PyObject *retval = NULL; /* Return value */
|
||||||
_PyRuntimeState * const runtime = &_PyRuntime;
|
|
||||||
struct _ceval_runtime_state * const ceval = &runtime->ceval;
|
|
||||||
struct _ceval_state * const ceval2 = &tstate->interp->ceval;
|
struct _ceval_state * const ceval2 = &tstate->interp->ceval;
|
||||||
_Py_atomic_int * const eval_breaker = &ceval2->eval_breaker;
|
_Py_atomic_int * const eval_breaker = &ceval2->eval_breaker;
|
||||||
PyCodeObject *co;
|
PyCodeObject *co;
|
||||||
|
@ -1276,39 +1374,7 @@ main_loop:
|
||||||
goto fast_next_opcode;
|
goto fast_next_opcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_Py_atomic_load_relaxed(&ceval->signals_pending)) {
|
if (eval_frame_handle_pending(tstate) != 0) {
|
||||||
if (handle_signals(tstate) != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_Py_atomic_load_relaxed(&ceval2->pending.calls_to_do)) {
|
|
||||||
if (make_pending_calls(tstate) != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
|
||||||
/* Give another thread a chance */
|
|
||||||
if (_PyThreadState_Swap(&runtime->gilstate, NULL) != tstate) {
|
|
||||||
Py_FatalError("tstate mix-up");
|
|
||||||
}
|
|
||||||
drop_gil(ceval, ceval2, tstate);
|
|
||||||
|
|
||||||
/* Other threads may run now */
|
|
||||||
|
|
||||||
take_gil(tstate);
|
|
||||||
|
|
||||||
if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
|
|
||||||
Py_FatalError("orphan tstate");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Check for asynchronous exceptions. */
|
|
||||||
if (tstate->async_exc != NULL) {
|
|
||||||
PyObject *exc = tstate->async_exc;
|
|
||||||
tstate->async_exc = NULL;
|
|
||||||
UNSIGNAL_ASYNC_EXC(ceval, ceval2);
|
|
||||||
_PyErr_SetNone(tstate, exc);
|
|
||||||
Py_DECREF(exc);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3989,7 +4055,7 @@ _PyEval_EvalCode(PyThreadState *tstate,
|
||||||
PyObject *kwdefs, PyObject *closure,
|
PyObject *kwdefs, PyObject *closure,
|
||||||
PyObject *name, PyObject *qualname)
|
PyObject *name, PyObject *qualname)
|
||||||
{
|
{
|
||||||
assert(tstate != NULL);
|
assert(is_tstate_valid(tstate));
|
||||||
|
|
||||||
PyCodeObject* co = (PyCodeObject*)_co;
|
PyCodeObject* co = (PyCodeObject*)_co;
|
||||||
PyFrameObject *f;
|
PyFrameObject *f;
|
||||||
|
@ -4642,7 +4708,7 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
|
||||||
int
|
int
|
||||||
_PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
_PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||||
{
|
{
|
||||||
assert(tstate != NULL);
|
assert(is_tstate_valid(tstate));
|
||||||
/* The caller must hold the GIL */
|
/* The caller must hold the GIL */
|
||||||
assert(PyGILState_Check());
|
assert(PyGILState_Check());
|
||||||
|
|
||||||
|
@ -4682,7 +4748,7 @@ PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
|
||||||
int
|
int
|
||||||
_PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
_PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||||
{
|
{
|
||||||
assert(tstate != NULL);
|
assert(is_tstate_valid(tstate));
|
||||||
/* The caller must hold the GIL */
|
/* The caller must hold the GIL */
|
||||||
assert(PyGILState_Check());
|
assert(PyGILState_Check());
|
||||||
|
|
||||||
|
@ -4692,9 +4758,9 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct _ceval_state *ceval = &tstate->interp->ceval;
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
PyObject *traceobj = tstate->c_traceobj;
|
PyObject *traceobj = tstate->c_traceobj;
|
||||||
ceval->tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL);
|
ceval2->tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL);
|
||||||
|
|
||||||
tstate->c_tracefunc = NULL;
|
tstate->c_tracefunc = NULL;
|
||||||
tstate->c_traceobj = NULL;
|
tstate->c_traceobj = NULL;
|
||||||
|
|
|
@ -141,8 +141,7 @@ static void recreate_gil(struct _gil_runtime_state *gil)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2,
|
drop_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate)
|
||||||
PyThreadState *tstate)
|
|
||||||
{
|
{
|
||||||
struct _gil_runtime_state *gil = &ceval->gil;
|
struct _gil_runtime_state *gil = &ceval->gil;
|
||||||
if (!_Py_atomic_load_relaxed(&gil->locked)) {
|
if (!_Py_atomic_load_relaxed(&gil->locked)) {
|
||||||
|
@ -169,7 +168,7 @@ drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2,
|
||||||
/* Not switched yet => wait */
|
/* Not switched yet => wait */
|
||||||
if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
|
if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
|
||||||
{
|
{
|
||||||
RESET_GIL_DROP_REQUEST(ceval, ceval2);
|
RESET_GIL_DROP_REQUEST(tstate);
|
||||||
/* NOTE: if COND_WAIT does not atomically start waiting when
|
/* NOTE: if COND_WAIT does not atomically start waiting when
|
||||||
releasing the mutex, another thread can run through, take
|
releasing the mutex, another thread can run through, take
|
||||||
the GIL and drop it again, and reset the condition
|
the GIL and drop it again, and reset the condition
|
||||||
|
@ -223,14 +222,9 @@ take_gil(PyThreadState *tstate)
|
||||||
PyThread_exit_thread();
|
PyThread_exit_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure that tstate is valid: sanity check for PyEval_AcquireThread() and
|
assert(is_tstate_valid(tstate));
|
||||||
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 _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
|
||||||
struct _gil_runtime_state *gil = &ceval->gil;
|
struct _gil_runtime_state *gil = &ceval->gil;
|
||||||
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
|
||||||
|
|
||||||
/* Check that _PyEval_InitThreads() was called to create the lock */
|
/* Check that _PyEval_InitThreads() was called to create the lock */
|
||||||
assert(gil_created(gil));
|
assert(gil_created(gil));
|
||||||
|
@ -259,7 +253,7 @@ take_gil(PyThreadState *tstate)
|
||||||
PyThread_exit_thread();
|
PyThread_exit_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
SET_GIL_DROP_REQUEST(ceval);
|
SET_GIL_DROP_REQUEST(tstate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,20 +286,21 @@ _ready:
|
||||||
in take_gil() while the main thread called
|
in take_gil() while the main thread called
|
||||||
wait_for_thread_shutdown() from Py_Finalize(). */
|
wait_for_thread_shutdown() from Py_Finalize(). */
|
||||||
MUTEX_UNLOCK(gil->mutex);
|
MUTEX_UNLOCK(gil->mutex);
|
||||||
drop_gil(ceval, ceval2, tstate);
|
drop_gil(ceval, tstate);
|
||||||
PyThread_exit_thread();
|
PyThread_exit_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
|
||||||
RESET_GIL_DROP_REQUEST(ceval, ceval2);
|
RESET_GIL_DROP_REQUEST(tstate);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* bpo-40010: eval_breaker should be recomputed to be set to 1 if there
|
/* bpo-40010: eval_breaker should be recomputed to be set to 1 if there
|
||||||
a pending signal: signal received by another thread which cannot
|
is a pending signal: signal received by another thread which cannot
|
||||||
handle signals.
|
handle signals.
|
||||||
|
|
||||||
Note: RESET_GIL_DROP_REQUEST() calls COMPUTE_EVAL_BREAKER(). */
|
Note: RESET_GIL_DROP_REQUEST() calls COMPUTE_EVAL_BREAKER(). */
|
||||||
COMPUTE_EVAL_BREAKER(ceval, ceval2);
|
struct _ceval_state *ceval2 = &tstate->interp->ceval;
|
||||||
|
COMPUTE_EVAL_BREAKER(tstate, ceval, ceval2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't access tstate if the thread must exit */
|
/* Don't access tstate if the thread must exit */
|
||||||
|
|
Loading…
Reference in New Issue