diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 0cd5550cfda..da6b87bd0f8 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -53,6 +53,22 @@ _Py_ThreadCanHandlePendingCalls(void) PyAPI_FUNC(PyThreadState*) _PyThreadState_GetTSS(void); #endif + +// Thread local storage for the current interpreter and the current Python +// thread state, if supported by the compiler. +#ifdef HAVE__THREAD_LOCAL_KEYWORD +// C11 _Thread_local keyword +# define _Py_HAVE_TSS_TSTATE +PyAPI_DATA(_Thread_local PyInterpreterState*) _Py_current_interp; +PyAPI_DATA(_Thread_local PyThreadState*) _Py_current_tstate; +#elif defined(__GNUC__) || defined(__clang__) +// GCC and clang __thread extension +# define _Py_HAVE_TSS_TSTATE +PyAPI_DATA(__thread PyInterpreterState*) _Py_current_interp; +PyAPI_DATA(__thread PyThreadState*) _Py_current_tstate; +#endif + + static inline PyThreadState* _PyRuntimeState_GetThreadState(_PyRuntimeState *runtime) { @@ -75,7 +91,9 @@ _PyRuntimeState_GetThreadState(_PyRuntimeState *runtime) static inline PyThreadState* _PyThreadState_GET(void) { -#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS +#ifdef _Py_HAVE_TSS_TSTATE + return _Py_current_tstate; +#elif defined(EXPERIMENTAL_ISOLATED_SUBINTERPRETERS) return _PyThreadState_GetTSS(); #else return _PyRuntimeState_GetThreadState(&_PyRuntime); @@ -110,11 +128,15 @@ _Py_EnsureFuncTstateNotNULL(const char *func, PyThreadState *tstate) See also _PyInterpreterState_Get() and _PyGILState_GetInterpreterStateUnsafe(). */ static inline PyInterpreterState* _PyInterpreterState_GET(void) { +#ifdef _Py_HAVE_TSS_TSTATE + return _Py_current_interp; +#else PyThreadState *tstate = _PyThreadState_GET(); #ifdef Py_DEBUG _Py_EnsureTstateNotNULL(tstate); #endif return tstate->interp; +#endif } diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-12-28-15-58-13.bpo-40522.Q5jhgq.rst b/Misc/NEWS.d/next/Core and Builtins/2020-12-28-15-58-13.bpo-40522.Q5jhgq.rst new file mode 100644 index 00000000000..6fef4fd402e --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-12-28-15-58-13.bpo-40522.Q5jhgq.rst @@ -0,0 +1,2 @@ +If Python is built with GCC or clang, the current interpreter and the current +Python thread state are now stored in a Thread Local Storage. diff --git a/Python/pystate.c b/Python/pystate.c index c791b239993..5fd1fbc5b96 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -36,9 +36,16 @@ extern "C" { #define _PyRuntimeGILState_GetThreadState(gilstate) \ ((PyThreadState*)_Py_atomic_load_relaxed(&(gilstate)->tstate_current)) -#define _PyRuntimeGILState_SetThreadState(gilstate, value) \ - _Py_atomic_store_relaxed(&(gilstate)->tstate_current, \ - (uintptr_t)(value)) + + +#ifdef HAVE__THREAD_LOCAL_KEYWORD +_Thread_local PyInterpreterState *_Py_current_interp = NULL; +_Thread_local PyThreadState *_Py_current_tstate = NULL; +#elif defined(__GNUC__) || defined(__clang__) +# define _Py_HAVE_TSS_TSTATE +__thread PyInterpreterState *_Py_current_interp = NULL; +__thread PyThreadState *_Py_current_tstate = NULL; +#endif /* Forward declarations */ static PyThreadState *_PyGILState_GetThisThreadState(struct _gilstate_runtime_state *gilstate); @@ -603,6 +610,22 @@ PyInterpreterState_GetDict(PyInterpreterState *interp) return interp->dict; } + +static void +set_current_tstate(struct _gilstate_runtime_state *gilstate, + PyThreadState *tstate) +{ +#ifdef _Py_HAVE_TSS_TSTATE + _Py_current_tstate = tstate; + _Py_current_interp = tstate ? tstate->interp : NULL; +#endif +#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS + PyThread_tss_set(&gilstate->autoTSSkey, tstate); +#endif + _Py_atomic_store_relaxed(&gilstate->tstate_current, (uintptr_t)tstate); +} + + static PyThreadState * new_threadstate(PyInterpreterState *interp, int init) { @@ -929,7 +952,7 @@ _PyThreadState_DeleteCurrent(PyThreadState *tstate) _Py_EnsureTstateNotNULL(tstate); struct _gilstate_runtime_state *gilstate = &tstate->interp->runtime->gilstate; tstate_delete_common(tstate, gilstate); - _PyRuntimeGILState_SetThreadState(gilstate, NULL); + set_current_tstate(gilstate, NULL); _PyEval_ReleaseLock(tstate); PyMem_RawFree(tstate); } @@ -1018,7 +1041,8 @@ _PyThreadState_Swap(struct _gilstate_runtime_state *gilstate, PyThreadState *new PyThreadState *oldts = _PyRuntimeGILState_GetThreadState(gilstate); #endif - _PyRuntimeGILState_SetThreadState(gilstate, newts); + set_current_tstate(gilstate, newts); + /* It should not be possible for more than one thread state to be used for a thread. Check this the best we can in debug builds. @@ -1034,9 +1058,6 @@ _PyThreadState_Swap(struct _gilstate_runtime_state *gilstate, PyThreadState *new Py_FatalError("Invalid thread state for this thread"); errno = err; } -#endif -#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS - PyThread_tss_set(&gilstate->autoTSSkey, newts); #endif return oldts; } diff --git a/configure b/configure index 530c04a0ede..48dc8844eda 100755 --- a/configure +++ b/configure @@ -17129,6 +17129,37 @@ $as_echo "#define HAVE_BUILTIN_ATOMIC 1" >>confdefs.h fi +# Check for _Thread_local keyword +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Thread_local keyword" >&5 +$as_echo_n "checking for _Thread_local keyword... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + + _Thread_local int val; + int main() { + val = 1; + return val; + } + + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + have__thread_local_keyword=yes +else + have__thread_local_keyword=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have__thread_local_keyword" >&5 +$as_echo "$have__thread_local_keyword" >&6; } + +if test "$have__thread_local_keyword" = yes; then + +$as_echo "#define HAVE__THREAD_LOCAL_KEYWORD 1" >>confdefs.h + +fi + # ensurepip option { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ensurepip" >&5 $as_echo_n "checking for ensurepip... " >&6; } diff --git a/configure.ac b/configure.ac index 39eadfedfba..d496860db00 100644 --- a/configure.ac +++ b/configure.ac @@ -5606,6 +5606,24 @@ if test "$have_builtin_atomic" = yes; then AC_DEFINE(HAVE_BUILTIN_ATOMIC, 1, [Has builtin __atomic_load_n() and __atomic_store_n() functions]) fi +# Check for _Thread_local keyword +AC_MSG_CHECKING(for _Thread_local keyword) +AC_LINK_IFELSE( +[ + AC_LANG_SOURCE([[ + _Thread_local int val; + int main() { + val = 1; + return val; + } + ]]) +],[have__thread_local_keyword=yes],[have__thread_local_keyword=no]) +AC_MSG_RESULT($have__thread_local_keyword) + +if test "$have__thread_local_keyword" = yes; then + AC_DEFINE(HAVE__THREAD_LOCAL_KEYWORD, 1, [Has C11 _Thread_local keyword]) +fi + # ensurepip option AC_MSG_CHECKING(for ensurepip) AC_ARG_WITH(ensurepip, diff --git a/pyconfig.h.in b/pyconfig.h.in index 045cbd53aee..1c10e562cb5 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1359,6 +1359,9 @@ /* Define to 1 if you have the `_getpty' function. */ #undef HAVE__GETPTY +/* Has C11 _Thread_local keyword */ +#undef HAVE__THREAD_LOCAL_KEYWORD + /* Define to 1 if `major', `minor', and `makedev' are declared in . */ #undef MAJOR_IN_MKDEV