diff options
-rw-r--r-- | Include/ceval.h | 5 | ||||
-rw-r--r-- | Include/internal/pycore_ceval.h | 53 | ||||
-rw-r--r-- | Include/internal/pycore_pystate.h | 59 | ||||
-rw-r--r-- | Modules/signalmodule.c | 23 | ||||
-rw-r--r-- | Python/ceval.c | 369 | ||||
-rw-r--r-- | Python/ceval_gil.h | 158 | ||||
-rw-r--r-- | Python/pylifecycle.c | 9 | ||||
-rw-r--r-- | Python/pystate.c | 16 |
8 files changed, 371 insertions, 321 deletions
diff --git a/Include/ceval.h b/Include/ceval.h index 11283c0..2d4b67d 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -58,7 +58,6 @@ PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); #endif PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg); -PyAPI_FUNC(void) _PyEval_SignalReceived(void); PyAPI_FUNC(int) Py_MakePendingCalls(void); /* Protection against deeply nested recursive calls @@ -192,9 +191,6 @@ PyAPI_FUNC(void) PyEval_RestoreThread(PyThreadState *); PyAPI_FUNC(int) PyEval_ThreadsInitialized(void); PyAPI_FUNC(void) PyEval_InitThreads(void); -#ifndef Py_LIMITED_API -PyAPI_FUNC(void) _PyEval_FiniThreads(void); -#endif /* !Py_LIMITED_API */ PyAPI_FUNC(void) PyEval_AcquireLock(void) Py_DEPRECATED(3.2); PyAPI_FUNC(void) PyEval_ReleaseLock(void) /* Py_DEPRECATED(3.2) */; PyAPI_FUNC(void) PyEval_AcquireThread(PyThreadState *tstate); @@ -221,7 +217,6 @@ PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc); #ifndef Py_LIMITED_API PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *); PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *); -PyAPI_FUNC(void) _PyEval_SignalAsyncExc(void); #endif /* Masks and values used by FORMAT_VALUE opcode. */ diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 0bb19f1..cdc73a3 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -9,50 +9,21 @@ extern "C" { #endif #include "pycore_atomic.h" +#include "pycore_pystate.h" #include "pythread.h" -PyAPI_FUNC(void) _Py_FinishPendingCalls(void); - -struct _pending_calls { - int finishing; - PyThread_type_lock lock; - /* Request for running pending calls. */ - _Py_atomic_int calls_to_do; - /* Request for looking at the `async_exc` field of the current - thread state. - Guarded by the GIL. */ - int async_exc; -#define NPENDINGCALLS 32 - struct { - int (*func)(void *); - void *arg; - } calls[NPENDINGCALLS]; - int first; - int last; -}; - -#include "pycore_gil.h" - -struct _ceval_runtime_state { - int recursion_limit; - /* Records whether tracing is on for any thread. Counts the number - of threads for which tstate->c_tracefunc is non-NULL, so if the - value is 0, we know we don't have to check this thread's - c_tracefunc. This speeds up the if statement in - PyEval_EvalFrameEx() after fast_next_opcode. */ - int tracing_possible; - /* This single variable consolidates all requests to break out of - the fast path in the eval loop. */ - _Py_atomic_int eval_breaker; - /* Request for dropping the GIL */ - _Py_atomic_int gil_drop_request; - struct _pending_calls pending; - /* Request for checking signals. */ - _Py_atomic_int signals_pending; - struct _gil_runtime_state gil; -}; - +PyAPI_FUNC(void) _Py_FinishPendingCalls(_PyRuntimeState *runtime); PyAPI_FUNC(void) _PyEval_Initialize(struct _ceval_runtime_state *); +PyAPI_FUNC(void) _PyEval_FiniThreads( + struct _ceval_runtime_state *ceval); +PyAPI_FUNC(void) _PyEval_SignalReceived( + struct _ceval_runtime_state *ceval); +PyAPI_FUNC(int) _PyEval_AddPendingCall( + struct _ceval_runtime_state *ceval, + int (*func)(void *), + void *arg); +PyAPI_FUNC(void) _PyEval_SignalAsyncExc( + struct _ceval_runtime_state *ceval); #ifdef __cplusplus } diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 69ceecb..1561328 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -12,12 +12,51 @@ extern "C" { #include "pystate.h" #include "pythread.h" -#include "pycore_ceval.h" +#include "pycore_gil.h" /* _gil_runtime_state */ #include "pycore_pathconfig.h" #include "pycore_pymem.h" #include "pycore_warnings.h" +/* ceval state */ + +struct _pending_calls { + int finishing; + PyThread_type_lock lock; + /* Request for running pending calls. */ + _Py_atomic_int calls_to_do; + /* Request for looking at the `async_exc` field of the current + thread state. + Guarded by the GIL. */ + int async_exc; +#define NPENDINGCALLS 32 + struct { + int (*func)(void *); + void *arg; + } calls[NPENDINGCALLS]; + int first; + int last; +}; + +struct _ceval_runtime_state { + int recursion_limit; + /* Records whether tracing is on for any thread. Counts the number + of threads for which tstate->c_tracefunc is non-NULL, so if the + value is 0, we know we don't have to check this thread's + c_tracefunc. This speeds up the if statement in + PyEval_EvalFrameEx() after fast_next_opcode. */ + int tracing_possible; + /* This single variable consolidates all requests to break out of + the fast path in the eval loop. */ + _Py_atomic_int eval_breaker; + /* Request for dropping the GIL */ + _Py_atomic_int gil_drop_request; + struct _pending_calls pending; + /* Request for checking signals. */ + _Py_atomic_int signals_pending; + struct _gil_runtime_state gil; +}; + /* interpreter state */ typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int); @@ -203,13 +242,16 @@ PyAPI_FUNC(_PyInitError) _PyRuntime_Initialize(void); PyAPI_FUNC(void) _PyRuntime_Finalize(void); -#define _Py_CURRENTLY_FINALIZING(tstate) \ - (_PyRuntime.finalizing == tstate) +#define _Py_CURRENTLY_FINALIZING(runtime, tstate) \ + (runtime->finalizing == tstate) /* Variable and macro for in-line access to current thread and interpreter state */ +#define _PyRuntimeState_GetThreadState(runtime) \ + ((PyThreadState*)_Py_atomic_load_relaxed(&(runtime)->gilstate.tstate_current)) + /* Get the current Python thread state. Efficient macro reading directly the 'gilstate.tstate_current' atomic @@ -219,8 +261,7 @@ PyAPI_FUNC(void) _PyRuntime_Finalize(void); The caller must hold the GIL. See also PyThreadState_Get() and PyThreadState_GET(). */ -#define _PyThreadState_GET() \ - ((PyThreadState*)_Py_atomic_load_relaxed(&_PyRuntime.gilstate.tstate_current)) +#define _PyThreadState_GET() _PyRuntimeState_GetThreadState(&_PyRuntime) /* Redefine PyThreadState_GET() as an alias to _PyThreadState_GET() */ #undef PyThreadState_GET @@ -242,7 +283,13 @@ PyAPI_FUNC(void) _PyRuntime_Finalize(void); PyAPI_FUNC(void) _PyThreadState_Init( _PyRuntimeState *runtime, PyThreadState *tstate); -PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate); +PyAPI_FUNC(void) _PyThreadState_DeleteExcept( + _PyRuntimeState *runtime, + PyThreadState *tstate); + +PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap( + struct _gilstate_runtime_state *gilstate, + PyThreadState *newts); PyAPI_FUNC(_PyInitError) _PyInterpreterState_Enable(_PyRuntimeState *runtime); PyAPI_FUNC(void) _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime); diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 43a4da1..b5e6250 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -5,6 +5,8 @@ #include "Python.h" #include "pycore_atomic.h" +#include "pycore_ceval.h" +#include "pycore_pystate.h" #ifndef MS_WINDOWS #include "posixmodule.h" @@ -256,7 +258,8 @@ trip_signal(int sig_num) _Py_atomic_store(&is_tripped, 1); /* Notify ceval.c */ - _PyEval_SignalReceived(); + _PyRuntimeState *runtime = &_PyRuntime; + _PyEval_SignalReceived(&runtime->ceval); /* And then write to the wakeup fd *after* setting all the globals and doing the _PyEval_SignalReceived. We used to write to the wakeup fd @@ -296,8 +299,9 @@ trip_signal(int sig_num) { /* Py_AddPendingCall() isn't signal-safe, but we still use it for this exceptional case. */ - Py_AddPendingCall(report_wakeup_send_error, - (void *)(intptr_t) last_error); + _PyEval_AddPendingCall(&runtime->ceval, + report_wakeup_send_error, + (void *)(intptr_t) last_error); } } } @@ -314,8 +318,9 @@ trip_signal(int sig_num) { /* Py_AddPendingCall() isn't signal-safe, but we still use it for this exceptional case. */ - Py_AddPendingCall(report_wakeup_write_error, - (void *)(intptr_t)errno); + _PyEval_AddPendingCall(&runtime->ceval, + report_wakeup_write_error, + (void *)(intptr_t)errno); } } } @@ -420,7 +425,7 @@ signal_raise_signal_impl(PyObject *module, int signalnum) err = raise(signalnum); _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS - + if (err) { return PyErr_SetFromErrno(PyExc_OSError); } @@ -1076,18 +1081,18 @@ fill_siginfo(siginfo_t *si) PyStructSequence_SET_ITEM(result, 0, PyLong_FromLong((long)(si->si_signo))); PyStructSequence_SET_ITEM(result, 1, PyLong_FromLong((long)(si->si_code))); -#ifdef __VXWORKS__ +#ifdef __VXWORKS__ PyStructSequence_SET_ITEM(result, 2, PyLong_FromLong(0L)); PyStructSequence_SET_ITEM(result, 3, PyLong_FromLong(0L)); PyStructSequence_SET_ITEM(result, 4, PyLong_FromLong(0L)); PyStructSequence_SET_ITEM(result, 5, PyLong_FromLong(0L)); -#else +#else PyStructSequence_SET_ITEM(result, 2, PyLong_FromLong((long)(si->si_errno))); PyStructSequence_SET_ITEM(result, 3, PyLong_FromPid(si->si_pid)); PyStructSequence_SET_ITEM(result, 4, _PyLong_FromUid(si->si_uid)); PyStructSequence_SET_ITEM(result, 5, PyLong_FromLong((long)(si->si_status))); -#endif +#endif #ifdef HAVE_SIGINFO_T_SI_BAND PyStructSequence_SET_ITEM(result, 6, PyLong_FromLong(si->si_band)); #else diff --git a/Python/ceval.c b/Python/ceval.c index 07db1d3..1743953 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -10,6 +10,7 @@ #define PY_LOCAL_AGGRESSIVE #include "Python.h" +#include "pycore_ceval.h" #include "pycore_object.h" #include "pycore_pystate.h" #include "pycore_tupleobject.h" @@ -40,9 +41,12 @@ extern int _PyObject_GetMethod(PyObject *, PyObject *, PyObject **); typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); /* Forward declarations */ -Py_LOCAL_INLINE(PyObject *) call_function(PyObject ***, Py_ssize_t, - PyObject *); -static PyObject * do_call_core(PyObject *, PyObject *, PyObject *); +Py_LOCAL_INLINE(PyObject *) call_function( + PyThreadState *tstate, PyObject ***pp_stack, + Py_ssize_t oparg, PyObject *kwnames); +static PyObject * do_call_core( + PyThreadState *tstate, PyObject *func, + PyObject *callargs, PyObject *kwdict); #ifdef LLTRACE static int lltrace; @@ -76,7 +80,6 @@ static PyObject * special_lookup(PyObject *, _Py_Identifier *); static int check_args_iterable(PyObject *func, PyObject *vararg); static void format_kwargs_error(PyObject *func, PyObject *kwargs); static void format_awaitable_error(PyTypeObject *, int); -static inline void exit_thread_if_finalizing(PyThreadState *); #define NAME_ERROR_MSG \ "name '%.200s' is not defined" @@ -96,66 +99,66 @@ static long dxp[256]; #endif #endif -#define GIL_REQUEST _Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request) +#define GIL_REQUEST _Py_atomic_load_relaxed(&ceval->gil_drop_request) /* 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 the GIL eventually anyway. */ -#define COMPUTE_EVAL_BREAKER() \ +#define COMPUTE_EVAL_BREAKER(ceval) \ _Py_atomic_store_relaxed( \ - &_PyRuntime.ceval.eval_breaker, \ + &(ceval)->eval_breaker, \ GIL_REQUEST | \ - _Py_atomic_load_relaxed(&_PyRuntime.ceval.signals_pending) | \ - _Py_atomic_load_relaxed(&_PyRuntime.ceval.pending.calls_to_do) | \ - _PyRuntime.ceval.pending.async_exc) + _Py_atomic_load_relaxed(&(ceval)->signals_pending) | \ + _Py_atomic_load_relaxed(&(ceval)->pending.calls_to_do) | \ + (ceval)->pending.async_exc) -#define SET_GIL_DROP_REQUEST() \ +#define SET_GIL_DROP_REQUEST(ceval) \ do { \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil_drop_request, 1); \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \ + _Py_atomic_store_relaxed(&(ceval)->gil_drop_request, 1); \ + _Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \ } while (0) -#define RESET_GIL_DROP_REQUEST() \ +#define RESET_GIL_DROP_REQUEST(ceval) \ do { \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil_drop_request, 0); \ - COMPUTE_EVAL_BREAKER(); \ + _Py_atomic_store_relaxed(&(ceval)->gil_drop_request, 0); \ + COMPUTE_EVAL_BREAKER(ceval); \ } while (0) /* Pending calls are only modified under pending_lock */ -#define SIGNAL_PENDING_CALLS() \ +#define SIGNAL_PENDING_CALLS(ceval) \ do { \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.pending.calls_to_do, 1); \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \ + _Py_atomic_store_relaxed(&(ceval)->pending.calls_to_do, 1); \ + _Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \ } while (0) -#define UNSIGNAL_PENDING_CALLS() \ +#define UNSIGNAL_PENDING_CALLS(ceval) \ do { \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.pending.calls_to_do, 0); \ - COMPUTE_EVAL_BREAKER(); \ + _Py_atomic_store_relaxed(&(ceval)->pending.calls_to_do, 0); \ + COMPUTE_EVAL_BREAKER(ceval); \ } while (0) -#define SIGNAL_PENDING_SIGNALS() \ +#define SIGNAL_PENDING_SIGNALS(ceval) \ do { \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.signals_pending, 1); \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \ + _Py_atomic_store_relaxed(&(ceval)->signals_pending, 1); \ + _Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \ } while (0) -#define UNSIGNAL_PENDING_SIGNALS() \ +#define UNSIGNAL_PENDING_SIGNALS(ceval) \ do { \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.signals_pending, 0); \ - COMPUTE_EVAL_BREAKER(); \ + _Py_atomic_store_relaxed(&(ceval)->signals_pending, 0); \ + COMPUTE_EVAL_BREAKER(ceval); \ } while (0) -#define SIGNAL_ASYNC_EXC() \ +#define SIGNAL_ASYNC_EXC(ceval) \ do { \ - _PyRuntime.ceval.pending.async_exc = 1; \ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.eval_breaker, 1); \ + (ceval)->pending.async_exc = 1; \ + _Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \ } while (0) -#define UNSIGNAL_ASYNC_EXC() \ +#define UNSIGNAL_ASYNC_EXC(ceval) \ do { \ - _PyRuntime.ceval.pending.async_exc = 0; \ - COMPUTE_EVAL_BREAKER(); \ + (ceval)->pending.async_exc = 0; \ + COMPUTE_EVAL_BREAKER(ceval); \ } while (0) @@ -168,48 +171,55 @@ static long dxp[256]; int PyEval_ThreadsInitialized(void) { - return gil_created(); + return gil_created(&_PyRuntime.ceval.gil); } void PyEval_InitThreads(void) { - if (gil_created()) { + _PyRuntimeState *runtime = &_PyRuntime; + struct _ceval_runtime_state *ceval = &runtime->ceval; + struct _gil_runtime_state *gil = &ceval->gil; + if (gil_created(gil)) { return; } PyThread_init_thread(); - create_gil(); - take_gil(_PyThreadState_GET()); + create_gil(gil); + PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); + take_gil(ceval, tstate); - _PyRuntime.ceval.pending.lock = PyThread_allocate_lock(); - if (_PyRuntime.ceval.pending.lock == NULL) { + struct _pending_calls *pending = &ceval->pending; + pending->lock = PyThread_allocate_lock(); + if (pending->lock == NULL) { Py_FatalError("Can't initialize threads for pending calls"); } } void -_PyEval_FiniThreads(void) +_PyEval_FiniThreads(struct _ceval_runtime_state *ceval) { - if (!gil_created()) { + struct _gil_runtime_state *gil = &ceval->gil; + if (!gil_created(gil)) { return; } - destroy_gil(); - assert(!gil_created()); + destroy_gil(gil); + assert(!gil_created(gil)); - if (_PyRuntime.ceval.pending.lock != NULL) { - PyThread_free_lock(_PyRuntime.ceval.pending.lock); - _PyRuntime.ceval.pending.lock = NULL; + struct _pending_calls *pending = &ceval->pending; + if (pending->lock != NULL) { + PyThread_free_lock(pending->lock); + pending->lock = NULL; } } static inline void -exit_thread_if_finalizing(PyThreadState *tstate) +exit_thread_if_finalizing(_PyRuntimeState *runtime, PyThreadState *tstate) { /* _Py_Finalizing is protected by the GIL */ - if (_Py_IsFinalizing() && !_Py_CURRENTLY_FINALIZING(tstate)) { - drop_gil(tstate); + if (runtime->finalizing != NULL && !_Py_CURRENTLY_FINALIZING(runtime, tstate)) { + drop_gil(&runtime->ceval, tstate); PyThread_exit_thread(); } } @@ -217,45 +227,60 @@ exit_thread_if_finalizing(PyThreadState *tstate) void PyEval_AcquireLock(void) { - PyThreadState *tstate = _PyThreadState_GET(); - if (tstate == NULL) + _PyRuntimeState *runtime = &_PyRuntime; + struct _ceval_runtime_state *ceval = &runtime->ceval; + PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); + if (tstate == NULL) { Py_FatalError("PyEval_AcquireLock: current thread state is NULL"); - take_gil(tstate); - exit_thread_if_finalizing(tstate); + } + take_gil(ceval, tstate); + exit_thread_if_finalizing(runtime, tstate); } void PyEval_ReleaseLock(void) { + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); /* This function must succeed when the current thread state is NULL. We therefore avoid PyThreadState_Get() which dumps a fatal error in debug mode. */ - drop_gil(_PyThreadState_GET()); + drop_gil(&runtime->ceval, tstate); } void PyEval_AcquireThread(PyThreadState *tstate) { - if (tstate == NULL) + if (tstate == NULL) { Py_FatalError("PyEval_AcquireThread: NULL new thread state"); + } + + _PyRuntimeState *runtime = &_PyRuntime; + struct _ceval_runtime_state *ceval = &runtime->ceval; + /* Check someone has called PyEval_InitThreads() to create the lock */ - assert(gil_created()); - take_gil(tstate); - exit_thread_if_finalizing(tstate); - if (PyThreadState_Swap(tstate) != NULL) - Py_FatalError( - "PyEval_AcquireThread: non-NULL old thread state"); + assert(gil_created(&ceval->gil)); + take_gil(ceval, tstate); + exit_thread_if_finalizing(runtime, tstate); + if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) { + Py_FatalError("PyEval_AcquireThread: non-NULL old thread state"); + } } void PyEval_ReleaseThread(PyThreadState *tstate) { - if (tstate == NULL) + if (tstate == NULL) { Py_FatalError("PyEval_ReleaseThread: NULL thread state"); - if (PyThreadState_Swap(NULL) != tstate) + } + + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *new_tstate = _PyThreadState_Swap(&runtime->gilstate, NULL); + if (new_tstate != tstate) { Py_FatalError("PyEval_ReleaseThread: wrong thread state"); - drop_gil(tstate); + } + drop_gil(&runtime->ceval, tstate); } /* This function is called from PyOS_AfterFork_Child to destroy all threads @@ -266,55 +291,65 @@ PyEval_ReleaseThread(PyThreadState *tstate) void PyEval_ReInitThreads(void) { - PyThreadState *current_tstate = _PyThreadState_GET(); - - if (!gil_created()) + _PyRuntimeState *runtime = &_PyRuntime; + struct _ceval_runtime_state *ceval = &runtime->ceval; + if (!gil_created(&ceval->gil)) { return; - recreate_gil(); - take_gil(current_tstate); + } + recreate_gil(&ceval->gil); + PyThreadState *current_tstate = _PyRuntimeState_GetThreadState(runtime); + take_gil(ceval, current_tstate); - _PyRuntime.ceval.pending.lock = PyThread_allocate_lock(); - if (_PyRuntime.ceval.pending.lock == NULL) { + struct _pending_calls *pending = &ceval->pending; + pending->lock = PyThread_allocate_lock(); + if (pending->lock == NULL) { Py_FatalError("Can't initialize threads for pending calls"); } /* Destroy all threads except the current one */ - _PyThreadState_DeleteExcept(current_tstate); + _PyThreadState_DeleteExcept(runtime, current_tstate); } /* This function is used to signal that async exceptions are waiting to be raised. */ void -_PyEval_SignalAsyncExc(void) +_PyEval_SignalAsyncExc(struct _ceval_runtime_state *ceval) { - SIGNAL_ASYNC_EXC(); + SIGNAL_ASYNC_EXC(ceval); } PyThreadState * PyEval_SaveThread(void) { - PyThreadState *tstate = PyThreadState_Swap(NULL); - if (tstate == NULL) + _PyRuntimeState *runtime = &_PyRuntime; + struct _ceval_runtime_state *ceval = &runtime->ceval; + PyThreadState *tstate = _PyThreadState_Swap(&runtime->gilstate, NULL); + if (tstate == NULL) { Py_FatalError("PyEval_SaveThread: NULL tstate"); - assert(gil_created()); - drop_gil(tstate); + } + assert(gil_created(&ceval->gil)); + drop_gil(ceval, tstate); return tstate; } void PyEval_RestoreThread(PyThreadState *tstate) { - if (tstate == NULL) + _PyRuntimeState *runtime = &_PyRuntime; + struct _ceval_runtime_state *ceval = &runtime->ceval; + + if (tstate == NULL) { Py_FatalError("PyEval_RestoreThread: NULL tstate"); - assert(gil_created()); + } + assert(gil_created(&ceval->gil)); int err = errno; - take_gil(tstate); - exit_thread_if_finalizing(tstate); + take_gil(ceval, tstate); + exit_thread_if_finalizing(runtime, tstate); errno = err; - PyThreadState_Swap(tstate); + _PyThreadState_Swap(&runtime->gilstate, tstate); } @@ -341,12 +376,12 @@ PyEval_RestoreThread(PyThreadState *tstate) */ void -_PyEval_SignalReceived(void) +_PyEval_SignalReceived(struct _ceval_runtime_state *ceval) { /* bpo-30703: Function called when the C signal handler of Python gets a signal. We cannot queue a callback using Py_AddPendingCall() since that function is not async-signal-safe. */ - SIGNAL_PENDING_SIGNALS(); + SIGNAL_PENDING_SIGNALS(ceval); } /* Push one item onto the queue while holding the lock. */ @@ -386,9 +421,10 @@ _pop_pending_call(struct _pending_calls *pending, */ int -Py_AddPendingCall(int (*func)(void *), void *arg) +_PyEval_AddPendingCall(struct _ceval_runtime_state *ceval, + int (*func)(void *), void *arg) { - struct _pending_calls *pending = &_PyRuntime.ceval.pending; + struct _pending_calls *pending = &ceval->pending; PyThread_acquire_lock(pending->lock, WAIT_LOCK); if (pending->finishing) { @@ -407,42 +443,50 @@ Py_AddPendingCall(int (*func)(void *), void *arg) PyThread_release_lock(pending->lock); /* signal main loop */ - SIGNAL_PENDING_CALLS(); + SIGNAL_PENDING_CALLS(ceval); return result; } +int +Py_AddPendingCall(int (*func)(void *), void *arg) +{ + return _PyEval_AddPendingCall(&_PyRuntime.ceval, func, arg); +} + static int -handle_signals(void) +handle_signals(_PyRuntimeState *runtime) { /* Only handle signals on main thread. PyEval_InitThreads must * have been called already. */ - if (PyThread_get_thread_ident() != _PyRuntime.main_thread) { + if (PyThread_get_thread_ident() != runtime->main_thread) { return 0; } /* * Ensure that the thread isn't currently running some other * interpreter. */ - if (_PyInterpreterState_GET_UNSAFE() != _PyRuntime.interpreters.main) { + PyInterpreterState *interp = _PyRuntimeState_GetThreadState(runtime)->interp; + if (interp != runtime->interpreters.main) { return 0; } - UNSIGNAL_PENDING_SIGNALS(); + struct _ceval_runtime_state *ceval = &runtime->ceval; + UNSIGNAL_PENDING_SIGNALS(ceval); if (_PyErr_CheckSignals() < 0) { - SIGNAL_PENDING_SIGNALS(); /* We're not done yet */ + SIGNAL_PENDING_SIGNALS(ceval); /* We're not done yet */ return -1; } return 0; } static int -make_pending_calls(struct _pending_calls* pending) +make_pending_calls(_PyRuntimeState *runtime) { static int busy = 0; /* only service pending calls on main thread */ - if (PyThread_get_thread_ident() != _PyRuntime.main_thread) { + if (PyThread_get_thread_ident() != runtime->main_thread) { return 0; } @@ -451,12 +495,14 @@ make_pending_calls(struct _pending_calls* pending) return 0; } busy = 1; + struct _ceval_runtime_state *ceval = &runtime->ceval; /* unsignal before starting to call callbacks, so that any callback added in-between re-signals */ - UNSIGNAL_PENDING_CALLS(); + UNSIGNAL_PENDING_CALLS(ceval); int res = 0; /* perform a bounded number of calls, in case of recursion */ + struct _pending_calls *pending = &ceval->pending; for (int i=0; i<NPENDINGCALLS; i++) { int (*func)(void *) = NULL; void *arg = NULL; @@ -481,17 +527,17 @@ make_pending_calls(struct _pending_calls* pending) error: busy = 0; - SIGNAL_PENDING_CALLS(); + SIGNAL_PENDING_CALLS(ceval); return res; } void -_Py_FinishPendingCalls(void) +_Py_FinishPendingCalls(_PyRuntimeState *runtime) { - struct _pending_calls *pending = &_PyRuntime.ceval.pending; - assert(PyGILState_Check()); + struct _pending_calls *pending = &runtime->ceval.pending; + PyThread_acquire_lock(pending->lock, WAIT_LOCK); pending->finishing = 1; PyThread_release_lock(pending->lock); @@ -500,7 +546,7 @@ _Py_FinishPendingCalls(void) return; } - if (make_pending_calls(pending) < 0) { + if (make_pending_calls(runtime) < 0) { PyObject *exc, *val, *tb; PyErr_Fetch(&exc, &val, &tb); PyErr_BadInternalCall(); @@ -518,12 +564,13 @@ Py_MakePendingCalls(void) /* Python signal handler doesn't really queue a callback: it only signals that a signal was received, see _PyEval_SignalReceived(). */ - int res = handle_signals(); + _PyRuntimeState *runtime = &_PyRuntime; + int res = handle_signals(runtime); if (res != 0) { return res; } - res = make_pending_calls(&_PyRuntime.ceval.pending); + res = make_pending_calls(runtime); if (res != 0) { return res; } @@ -556,8 +603,9 @@ Py_GetRecursionLimit(void) void Py_SetRecursionLimit(int new_limit) { - _PyRuntime.ceval.recursion_limit = new_limit; - _Py_CheckRecursionLimit = _PyRuntime.ceval.recursion_limit; + struct _ceval_runtime_state *ceval = &_PyRuntime.ceval; + ceval->recursion_limit = new_limit; + _Py_CheckRecursionLimit = ceval->recursion_limit; } /* the macro Py_EnterRecursiveCall() only calls _Py_CheckRecursiveCall() @@ -568,8 +616,9 @@ Py_SetRecursionLimit(int new_limit) int _Py_CheckRecursiveCall(const char *where) { - PyThreadState *tstate = _PyThreadState_GET(); - int recursion_limit = _PyRuntime.ceval.recursion_limit; + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); + int recursion_limit = runtime->ceval.recursion_limit; #ifdef USE_STACKCHECK tstate->stackcheck_counter = 0; @@ -602,10 +651,10 @@ _Py_CheckRecursiveCall(const char *where) return 0; } -static int do_raise(PyObject *, PyObject *); +static int do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause); static int unpack_iterable(PyObject *, int, int, PyObject **); -#define _Py_TracingPossible _PyRuntime.ceval.tracing_possible +#define _Py_TracingPossible(ceval) ((ceval)->tracing_possible) PyObject * @@ -649,8 +698,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) int oparg; /* Current opcode argument, if any */ PyObject **fastlocals, **freevars; PyObject *retval = NULL; /* Return value */ - PyThreadState *tstate = _PyThreadState_GET(); - _Py_atomic_int *eval_breaker = &_PyRuntime.ceval.eval_breaker; + _PyRuntimeState * const runtime = &_PyRuntime; + PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime); + struct _ceval_runtime_state * const ceval = &runtime->ceval; + _Py_atomic_int * const eval_breaker = &ceval->eval_breaker; PyCodeObject *co; /* when tracing we set things up so that @@ -734,18 +785,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) op: \ TARGET_##op -#define DISPATCH() \ - { \ - if (!_Py_atomic_load_relaxed(eval_breaker)) { \ - FAST_DISPATCH(); \ - } \ - continue; \ - } - #ifdef LLTRACE #define FAST_DISPATCH() \ { \ - if (!lltrace && !_Py_TracingPossible && !PyDTrace_LINE_ENABLED()) { \ + if (!lltrace && !_Py_TracingPossible(ceval) && !PyDTrace_LINE_ENABLED()) { \ f->f_lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ goto *opcode_targets[opcode]; \ @@ -755,7 +798,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) #else #define FAST_DISPATCH() \ { \ - if (!_Py_TracingPossible && !PyDTrace_LINE_ENABLED()) { \ + if (!_Py_TracingPossible(ceval) && !PyDTrace_LINE_ENABLED()) { \ f->f_lasti = INSTR_OFFSET(); \ NEXTOPARG(); \ goto *opcode_targets[opcode]; \ @@ -764,11 +807,18 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } #endif +#define DISPATCH() \ + { \ + if (!_Py_atomic_load_relaxed(eval_breaker)) { \ + FAST_DISPATCH(); \ + } \ + continue; \ + } + #else #define TARGET(op) op - -#define DISPATCH() continue #define FAST_DISPATCH() goto fast_next_opcode +#define DISPATCH() continue #endif @@ -1063,44 +1113,40 @@ main_loop: goto fast_next_opcode; } - if (_Py_atomic_load_relaxed( - &_PyRuntime.ceval.signals_pending)) - { - if (handle_signals() != 0) { + if (_Py_atomic_load_relaxed(&ceval->signals_pending)) { + if (handle_signals(runtime) != 0) { goto error; } } - if (_Py_atomic_load_relaxed( - &_PyRuntime.ceval.pending.calls_to_do)) - { - if (make_pending_calls(&_PyRuntime.ceval.pending) != 0) { + if (_Py_atomic_load_relaxed(&ceval->pending.calls_to_do)) { + if (make_pending_calls(runtime) != 0) { goto error; } } - if (_Py_atomic_load_relaxed( - &_PyRuntime.ceval.gil_drop_request)) - { + if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) { /* Give another thread a chance */ - if (PyThreadState_Swap(NULL) != tstate) + if (_PyThreadState_Swap(&runtime->gilstate, NULL) != tstate) { Py_FatalError("ceval: tstate mix-up"); - drop_gil(tstate); + } + drop_gil(ceval, tstate); /* Other threads may run now */ - take_gil(tstate); + take_gil(ceval, tstate); /* Check if we should make a quick exit. */ - exit_thread_if_finalizing(tstate); + exit_thread_if_finalizing(runtime, tstate); - if (PyThreadState_Swap(tstate) != NULL) + if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) { Py_FatalError("ceval: orphan tstate"); + } } /* Check for asynchronous exceptions. */ if (tstate->async_exc != NULL) { PyObject *exc = tstate->async_exc; tstate->async_exc = NULL; - UNSIGNAL_ASYNC_EXC(); + UNSIGNAL_ASYNC_EXC(ceval); PyErr_SetNone(exc); Py_DECREF(exc); goto error; @@ -1115,7 +1161,7 @@ main_loop: /* line-by-line tracing support */ - if (_Py_TracingPossible && + if (_Py_TracingPossible(ceval) && tstate->c_tracefunc != NULL && !tstate->tracing) { int err; /* see maybe_call_line_trace @@ -1740,7 +1786,7 @@ main_loop: exc = POP(); /* exc */ /* fall through */ case 0: - if (do_raise(exc, cause)) { + if (do_raise(tstate, exc, cause)) { goto exception_unwind; } break; @@ -3268,7 +3314,7 @@ main_loop: `callable` will be POPed by call_function. NULL will will be POPed manually later. */ - res = call_function(&sp, oparg, NULL); + res = call_function(tstate, &sp, oparg, NULL); stack_pointer = sp; (void)POP(); /* POP the NULL. */ } @@ -3285,7 +3331,7 @@ main_loop: We'll be passing `oparg + 1` to call_function, to make it accept the `self` as a first argument. */ - res = call_function(&sp, oparg + 1, NULL); + res = call_function(tstate, &sp, oparg + 1, NULL); stack_pointer = sp; } @@ -3299,7 +3345,7 @@ main_loop: PREDICTED(CALL_FUNCTION); PyObject **sp, *res; sp = stack_pointer; - res = call_function(&sp, oparg, NULL); + res = call_function(tstate, &sp, oparg, NULL); stack_pointer = sp; PUSH(res); if (res == NULL) { @@ -3314,7 +3360,7 @@ main_loop: names = POP(); assert(PyTuple_CheckExact(names) && PyTuple_GET_SIZE(names) <= oparg); sp = stack_pointer; - res = call_function(&sp, oparg, names); + res = call_function(tstate, &sp, oparg, names); stack_pointer = sp; PUSH(res); Py_DECREF(names); @@ -3358,7 +3404,7 @@ main_loop: } assert(PyTuple_CheckExact(callargs)); - result = do_call_core(func, callargs, kwargs); + result = do_call_core(tstate, func, callargs, kwargs); Py_DECREF(func); Py_DECREF(callargs); Py_XDECREF(kwargs); @@ -3855,7 +3901,6 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyFrameObject *f; PyObject *retval = NULL; PyObject **fastlocals, **freevars; - PyThreadState *tstate; PyObject *x, *u; const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount + co->co_posonlyargcount; Py_ssize_t i, j, n; @@ -3868,7 +3913,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, } /* Create the frame */ - tstate = _PyThreadState_GET(); + PyThreadState *tstate = _PyThreadState_GET(); assert(tstate != NULL); f = _PyFrame_New_NoTrack(tstate, co, globals, locals); if (f == NULL) { @@ -4180,13 +4225,12 @@ special_lookup(PyObject *o, _Py_Identifier *id) /* Logic for the raise statement (too complicated for inlining). This *consumes* a reference count to each of its arguments. */ static int -do_raise(PyObject *exc, PyObject *cause) +do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause) { PyObject *type = NULL, *value = NULL; if (exc == NULL) { /* Reraise */ - PyThreadState *tstate = _PyThreadState_GET(); _PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate); PyObject *tb; type = exc_info->exc_type; @@ -4529,9 +4573,10 @@ PyEval_SetProfile(Py_tracefunc func, PyObject *arg) void PyEval_SetTrace(Py_tracefunc func, PyObject *arg) { - PyThreadState *tstate = _PyThreadState_GET(); + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); PyObject *temp = tstate->c_traceobj; - _Py_TracingPossible += (func != NULL) - (tstate->c_tracefunc != NULL); + runtime->ceval.tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL); Py_XINCREF(arg); tstate->c_tracefunc = NULL; tstate->c_traceobj = NULL; @@ -4564,7 +4609,6 @@ void _PyEval_SetCoroutineWrapper(PyObject *wrapper) { PyThreadState *tstate = _PyThreadState_GET(); - Py_XINCREF(wrapper); Py_XSETREF(tstate->coroutine_wrapper, wrapper); } @@ -4580,7 +4624,6 @@ void _PyEval_SetAsyncGenFirstiter(PyObject *firstiter) { PyThreadState *tstate = _PyThreadState_GET(); - Py_XINCREF(firstiter); Py_XSETREF(tstate->async_gen_firstiter, firstiter); } @@ -4596,7 +4639,6 @@ void _PyEval_SetAsyncGenFinalizer(PyObject *finalizer) { PyThreadState *tstate = _PyThreadState_GET(); - Py_XINCREF(finalizer); Py_XSETREF(tstate->async_gen_finalizer, finalizer); } @@ -4662,8 +4704,9 @@ PyEval_GetGlobals(void) PyFrameObject * PyEval_GetFrame(void) { - PyThreadState *tstate = _PyThreadState_GET(); - return _PyThreadState_GetFrame(tstate); + _PyRuntimeState *runtime = &_PyRuntime; + PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); + return runtime->gilstate.getframe(tstate); } int @@ -4750,7 +4793,7 @@ if (tstate->use_tracing && tstate->c_profilefunc) { \ /* Issue #29227: Inline call_function() into _PyEval_EvalFrameDefault() to reduce the stack consumption. */ Py_LOCAL_INLINE(PyObject *) _Py_HOT_FUNCTION -call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) +call_function(PyThreadState *tstate, PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) { PyObject **pfunc = (*pp_stack) - oparg - 1; PyObject *func = *pfunc; @@ -4763,11 +4806,9 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) presumed to be the most frequent callable object. */ if (PyCFunction_Check(func)) { - PyThreadState *tstate = _PyThreadState_GET(); C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames)); } else if (Py_TYPE(func) == &PyMethodDescr_Type) { - PyThreadState *tstate = _PyThreadState_GET(); if (nargs > 0 && tstate->use_tracing) { /* We need to create a temporary bound method as argument for profiling. @@ -4832,17 +4873,15 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) } static PyObject * -do_call_core(PyObject *func, PyObject *callargs, PyObject *kwdict) +do_call_core(PyThreadState *tstate, PyObject *func, PyObject *callargs, PyObject *kwdict) { PyObject *result; if (PyCFunction_Check(func)) { - PyThreadState *tstate = _PyThreadState_GET(); C_TRACE(result, PyCFunction_Call(func, callargs, kwdict)); return result; } else if (Py_TYPE(func) == &PyMethodDescr_Type) { - PyThreadState *tstate = _PyThreadState_GET(); Py_ssize_t nargs = PyTuple_GET_SIZE(callargs); if (nargs > 0 && tstate->use_tracing) { /* We need to create a temporary bound method as argument diff --git a/Python/ceval_gil.h b/Python/ceval_gil.h index f2d5fdb..34d48c9 100644 --- a/Python/ceval_gil.h +++ b/Python/ceval_gil.h @@ -7,10 +7,6 @@ #include "pycore_atomic.h" -/* First some general settings */ - -#define INTERVAL (_PyRuntime.ceval.gil.interval >= 1 ? _PyRuntime.ceval.gil.interval : 1) - /* Notes about the implementation: @@ -94,158 +90,156 @@ #define DEFAULT_INTERVAL 5000 -static void _gil_initialize(struct _gil_runtime_state *state) +static void _gil_initialize(struct _gil_runtime_state *gil) { _Py_atomic_int uninitialized = {-1}; - state->locked = uninitialized; - state->interval = DEFAULT_INTERVAL; + gil->locked = uninitialized; + gil->interval = DEFAULT_INTERVAL; } -static int gil_created(void) +static int gil_created(struct _gil_runtime_state *gil) { - return (_Py_atomic_load_explicit(&_PyRuntime.ceval.gil.locked, - _Py_memory_order_acquire) - ) >= 0; + return (_Py_atomic_load_explicit(&gil->locked, _Py_memory_order_acquire) >= 0); } -static void create_gil(void) +static void create_gil(struct _gil_runtime_state *gil) { - MUTEX_INIT(_PyRuntime.ceval.gil.mutex); + MUTEX_INIT(gil->mutex); #ifdef FORCE_SWITCHING - MUTEX_INIT(_PyRuntime.ceval.gil.switch_mutex); + MUTEX_INIT(gil->switch_mutex); #endif - COND_INIT(_PyRuntime.ceval.gil.cond); + COND_INIT(gil->cond); #ifdef FORCE_SWITCHING - COND_INIT(_PyRuntime.ceval.gil.switch_cond); + COND_INIT(gil->switch_cond); #endif - _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder, 0); - _Py_ANNOTATE_RWLOCK_CREATE(&_PyRuntime.ceval.gil.locked); - _Py_atomic_store_explicit(&_PyRuntime.ceval.gil.locked, 0, - _Py_memory_order_release); + _Py_atomic_store_relaxed(&gil->last_holder, 0); + _Py_ANNOTATE_RWLOCK_CREATE(&gil->locked); + _Py_atomic_store_explicit(&gil->locked, 0, _Py_memory_order_release); } -static void destroy_gil(void) +static void destroy_gil(struct _gil_runtime_state *gil) { /* some pthread-like implementations tie the mutex to the cond * and must have the cond destroyed first. */ - COND_FINI(_PyRuntime.ceval.gil.cond); - MUTEX_FINI(_PyRuntime.ceval.gil.mutex); + COND_FINI(gil->cond); + MUTEX_FINI(gil->mutex); #ifdef FORCE_SWITCHING - COND_FINI(_PyRuntime.ceval.gil.switch_cond); - MUTEX_FINI(_PyRuntime.ceval.gil.switch_mutex); + COND_FINI(gil->switch_cond); + MUTEX_FINI(gil->switch_mutex); #endif - _Py_atomic_store_explicit(&_PyRuntime.ceval.gil.locked, -1, + _Py_atomic_store_explicit(&gil->locked, -1, _Py_memory_order_release); - _Py_ANNOTATE_RWLOCK_DESTROY(&_PyRuntime.ceval.gil.locked); + _Py_ANNOTATE_RWLOCK_DESTROY(&gil->locked); } -static void recreate_gil(void) +static void recreate_gil(struct _gil_runtime_state *gil) { - _Py_ANNOTATE_RWLOCK_DESTROY(&_PyRuntime.ceval.gil.locked); + _Py_ANNOTATE_RWLOCK_DESTROY(&gil->locked); /* XXX should we destroy the old OS resources here? */ - create_gil(); + create_gil(gil); } -static void drop_gil(PyThreadState *tstate) +static void +drop_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate) { - if (!_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked)) + struct _gil_runtime_state *gil = &ceval->gil; + if (!_Py_atomic_load_relaxed(&gil->locked)) { Py_FatalError("drop_gil: GIL is not locked"); + } + /* tstate is allowed to be NULL (early interpreter init) */ if (tstate != NULL) { /* Sub-interpreter support: threads might have been switched under our feet using PyThreadState_Swap(). Fix the GIL last holder variable so that our heuristics work. */ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder, - (uintptr_t)tstate); + _Py_atomic_store_relaxed(&gil->last_holder, (uintptr_t)tstate); } - MUTEX_LOCK(_PyRuntime.ceval.gil.mutex); - _Py_ANNOTATE_RWLOCK_RELEASED(&_PyRuntime.ceval.gil.locked, /*is_write=*/1); - _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.locked, 0); - COND_SIGNAL(_PyRuntime.ceval.gil.cond); - MUTEX_UNLOCK(_PyRuntime.ceval.gil.mutex); + MUTEX_LOCK(gil->mutex); + _Py_ANNOTATE_RWLOCK_RELEASED(&gil->locked, /*is_write=*/1); + _Py_atomic_store_relaxed(&gil->locked, 0); + COND_SIGNAL(gil->cond); + MUTEX_UNLOCK(gil->mutex); #ifdef FORCE_SWITCHING - if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request) && - tstate != NULL) - { - MUTEX_LOCK(_PyRuntime.ceval.gil.switch_mutex); + if (_Py_atomic_load_relaxed(&ceval->gil_drop_request) && tstate != NULL) { + MUTEX_LOCK(gil->switch_mutex); /* Not switched yet => wait */ - if (((PyThreadState*)_Py_atomic_load_relaxed( - &_PyRuntime.ceval.gil.last_holder) - ) == tstate) + if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate) { - RESET_GIL_DROP_REQUEST(); + RESET_GIL_DROP_REQUEST(ceval); /* NOTE: if COND_WAIT does not atomically start waiting when releasing the mutex, another thread can run through, take the GIL and drop it again, and reset the condition before we even had a chance to wait for it. */ - COND_WAIT(_PyRuntime.ceval.gil.switch_cond, - _PyRuntime.ceval.gil.switch_mutex); - } - MUTEX_UNLOCK(_PyRuntime.ceval.gil.switch_mutex); + COND_WAIT(gil->switch_cond, gil->switch_mutex); + } + MUTEX_UNLOCK(gil->switch_mutex); } #endif } -static void take_gil(PyThreadState *tstate) +static void +take_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate) { - int err; - if (tstate == NULL) + if (tstate == NULL) { Py_FatalError("take_gil: NULL tstate"); + } - err = errno; - MUTEX_LOCK(_PyRuntime.ceval.gil.mutex); + struct _gil_runtime_state *gil = &ceval->gil; + int err = errno; + MUTEX_LOCK(gil->mutex); - if (!_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked)) + if (!_Py_atomic_load_relaxed(&gil->locked)) { goto _ready; + } - while (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked)) { + while (_Py_atomic_load_relaxed(&gil->locked)) { int timed_out = 0; unsigned long saved_switchnum; - saved_switchnum = _PyRuntime.ceval.gil.switch_number; - COND_TIMED_WAIT(_PyRuntime.ceval.gil.cond, _PyRuntime.ceval.gil.mutex, - INTERVAL, timed_out); + saved_switchnum = gil->switch_number; + + + unsigned long interval = (gil->interval >= 1 ? gil->interval : 1); + COND_TIMED_WAIT(gil->cond, gil->mutex, interval, timed_out); /* If we timed out and no switch occurred in the meantime, it is time to ask the GIL-holding thread to drop it. */ if (timed_out && - _Py_atomic_load_relaxed(&_PyRuntime.ceval.gil.locked) && - _PyRuntime.ceval.gil.switch_number == saved_switchnum) { - SET_GIL_DROP_REQUEST(); + _Py_atomic_load_relaxed(&gil->locked) && + gil->switch_number == saved_switchnum) + { + SET_GIL_DROP_REQUEST(ceval); } } _ready: #ifdef FORCE_SWITCHING - /* This mutex must be taken before modifying - _PyRuntime.ceval.gil.last_holder (see drop_gil()). */ - MUTEX_LOCK(_PyRuntime.ceval.gil.switch_mutex); + /* This mutex must be taken before modifying gil->last_holder: + see drop_gil(). */ + MUTEX_LOCK(gil->switch_mutex); #endif /* We now hold the GIL */ - _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.locked, 1); - _Py_ANNOTATE_RWLOCK_ACQUIRED(&_PyRuntime.ceval.gil.locked, /*is_write=*/1); - - if (tstate != (PyThreadState*)_Py_atomic_load_relaxed( - &_PyRuntime.ceval.gil.last_holder)) - { - _Py_atomic_store_relaxed(&_PyRuntime.ceval.gil.last_holder, - (uintptr_t)tstate); - ++_PyRuntime.ceval.gil.switch_number; + _Py_atomic_store_relaxed(&gil->locked, 1); + _Py_ANNOTATE_RWLOCK_ACQUIRED(&gil->locked, /*is_write=*/1); + + if (tstate != (PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) { + _Py_atomic_store_relaxed(&gil->last_holder, (uintptr_t)tstate); + ++gil->switch_number; } #ifdef FORCE_SWITCHING - COND_SIGNAL(_PyRuntime.ceval.gil.switch_cond); - MUTEX_UNLOCK(_PyRuntime.ceval.gil.switch_mutex); + COND_SIGNAL(gil->switch_cond); + MUTEX_UNLOCK(gil->switch_mutex); #endif - if (_Py_atomic_load_relaxed(&_PyRuntime.ceval.gil_drop_request)) { - RESET_GIL_DROP_REQUEST(); + if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) { + RESET_GIL_DROP_REQUEST(ceval); } if (tstate->async_exc != NULL) { - _PyEval_SignalAsyncExc(); + _PyEval_SignalAsyncExc(ceval); } - MUTEX_UNLOCK(_PyRuntime.ceval.gil.mutex); + MUTEX_UNLOCK(gil->mutex); errno = err; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 32902aa..de8595c 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -4,8 +4,9 @@ #include "Python-ast.h" #undef Yield /* undefine macro conflicting with <winbase.h> */ -#include "pycore_coreconfig.h" +#include "pycore_ceval.h" #include "pycore_context.h" +#include "pycore_coreconfig.h" #include "pycore_fileutils.h" #include "pycore_hamt.h" #include "pycore_pathconfig.h" @@ -527,7 +528,7 @@ pycore_create_interpreter(_PyRuntimeState *runtime, another running thread (see issue #9901). Instead we destroy the previously created GIL here, which ensures that we can call Py_Initialize / Py_FinalizeEx multiple times. */ - _PyEval_FiniThreads(); + _PyEval_FiniThreads(&runtime->ceval); /* Auto-thread-state API */ _PyGILState_Init(runtime, interp, tstate); @@ -1135,10 +1136,10 @@ Py_FinalizeEx(void) wait_for_thread_shutdown(); // Make any remaining pending calls. - _Py_FinishPendingCalls(); + _Py_FinishPendingCalls(runtime); /* Get current thread state and interpreter pointer */ - PyThreadState *tstate = _PyThreadState_GET(); + PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); PyInterpreterState *interp = tstate->interp; /* The interpreter is still entirely intact at this point, and the diff --git a/Python/pystate.c b/Python/pystate.c index 44acfed..6731575 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2,6 +2,7 @@ /* Thread and interpreter state structures and their interfaces */ #include "Python.h" +#include "pycore_ceval.h" #include "pycore_coreconfig.h" #include "pycore_pymem.h" #include "pycore_pystate.h" @@ -39,7 +40,6 @@ extern "C" { /* Forward declarations */ static PyThreadState *_PyGILState_GetThisThreadState(struct _gilstate_runtime_state *gilstate); static void _PyThreadState_Delete(_PyRuntimeState *runtime, PyThreadState *tstate); -static PyThreadState *_PyThreadState_Swap(struct _gilstate_runtime_state *gilstate, PyThreadState *newts); static _PyInitError @@ -867,9 +867,8 @@ PyThreadState_DeleteCurrent() * be kept in those other interpreteres. */ void -_PyThreadState_DeleteExcept(PyThreadState *tstate) +_PyThreadState_DeleteExcept(_PyRuntimeState *runtime, PyThreadState *tstate) { - _PyRuntimeState *runtime = &_PyRuntime; PyInterpreterState *interp = tstate->interp; PyThreadState *p, *next, *garbage; HEAD_LOCK(runtime); @@ -915,7 +914,7 @@ PyThreadState_Get(void) } -static PyThreadState * +PyThreadState * _PyThreadState_Swap(struct _gilstate_runtime_state *gilstate, PyThreadState *newts) { PyThreadState *oldts = _PyRuntimeGILState_GetThreadState(gilstate); @@ -980,8 +979,8 @@ PyThreadState_GetDict(void) int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) { - PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); - PyThreadState *p; + _PyRuntimeState *runtime = &_PyRuntime; + PyInterpreterState *interp = _PyRuntimeState_GetThreadState(runtime)->interp; /* Although the GIL is held, a few C API functions can be called * without the GIL held, and in particular some that create and @@ -989,9 +988,8 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) * list of thread states we're traversing, so to prevent that we lock * head_mutex for the duration. */ - _PyRuntimeState *runtime = &_PyRuntime; HEAD_LOCK(runtime); - for (p = interp->tstate_head; p != NULL; p = p->next) { + for (PyThreadState *p = interp->tstate_head; p != NULL; p = p->next) { if (p->thread_id == id) { /* Tricky: we need to decref the current value * (if any) in p->async_exc, but that can in turn @@ -1005,7 +1003,7 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) p->async_exc = exc; HEAD_UNLOCK(runtime); Py_XDECREF(old_exc); - _PyEval_SignalAsyncExc(); + _PyEval_SignalAsyncExc(&runtime->ceval); return 1; } } |