bpo-40522: Store tstate in a Thread Local Storage

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.

Changes:

* configure checks for C11 _Thread_local keyword.
* Use _Thread_local keyword, GCC and clang __thread extension.
* Add set_current_tstate() sub-function which sets these two new TLS
  variables (if available).
* _PyThreadState_Swap() and _PyThreadState_DeleteCurrent() now call
  set_current_tstate().
* _PyThreadState_GET() and _PyInterpreterState_GET() now use the TLS
  variable if available.
This commit is contained in:
Victor Stinner 2020-12-28 15:10:16 +01:00
parent bf64d9064a
commit fd097fa8e8
6 changed files with 106 additions and 9 deletions

View File

@ -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
}

View File

@ -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.

View File

@ -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;
}

31
configure vendored
View File

@ -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; }

View File

@ -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,

View File

@ -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 <mkdev.h>.
*/
#undef MAJOR_IN_MKDEV