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:
parent
bf64d9064a
commit
fd097fa8e8
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
18
configure.ac
18
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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue