mirror of
https://github.com/python/cpython.git
synced 2025-08-30 21:48:47 +00:00
bpo-25658: Implement PEP 539 for Thread Specific Storage (TSS) API (GH-1362)
See PEP 539 for details. Highlights of changes: - Add Thread Specific Storage (TSS) API - Document the Thread Local Storage (TLS) API as deprecated - Update code that used TLS API to use TSS API
This commit is contained in:
parent
b8ab9d3fc8
commit
731e189014
18 changed files with 651 additions and 108 deletions
|
@ -46,7 +46,12 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
|||
_PyEval_Initialize(&runtime->ceval);
|
||||
|
||||
runtime->gilstate.check_enabled = 1;
|
||||
runtime->gilstate.autoTLSkey = -1;
|
||||
/* A TSS key must be initialized with Py_tss_NEEDS_INIT
|
||||
in accordance with the specification. */
|
||||
{
|
||||
Py_tss_t initial = Py_tss_NEEDS_INIT;
|
||||
runtime->gilstate.autoTSSkey = initial;
|
||||
}
|
||||
|
||||
runtime->interpreters.mutex = PyThread_allocate_lock();
|
||||
if (runtime->interpreters.mutex == NULL)
|
||||
|
@ -485,9 +490,9 @@ PyThreadState_Delete(PyThreadState *tstate)
|
|||
if (tstate == GET_TSTATE())
|
||||
Py_FatalError("PyThreadState_Delete: tstate is still current");
|
||||
if (_PyRuntime.gilstate.autoInterpreterState &&
|
||||
PyThread_get_key_value(_PyRuntime.gilstate.autoTLSkey) == tstate)
|
||||
PyThread_tss_get(&_PyRuntime.gilstate.autoTSSkey) == tstate)
|
||||
{
|
||||
PyThread_delete_key_value(_PyRuntime.gilstate.autoTLSkey);
|
||||
PyThread_tss_set(&_PyRuntime.gilstate.autoTSSkey, NULL);
|
||||
}
|
||||
tstate_delete_common(tstate);
|
||||
}
|
||||
|
@ -502,9 +507,9 @@ PyThreadState_DeleteCurrent()
|
|||
"PyThreadState_DeleteCurrent: no current tstate");
|
||||
tstate_delete_common(tstate);
|
||||
if (_PyRuntime.gilstate.autoInterpreterState &&
|
||||
PyThread_get_key_value(_PyRuntime.gilstate.autoTLSkey) == tstate)
|
||||
PyThread_tss_get(&_PyRuntime.gilstate.autoTSSkey) == tstate)
|
||||
{
|
||||
PyThread_delete_key_value(_PyRuntime.gilstate.autoTLSkey);
|
||||
PyThread_tss_set(&_PyRuntime.gilstate.autoTSSkey, NULL);
|
||||
}
|
||||
SET_TSTATE(NULL);
|
||||
PyEval_ReleaseLock();
|
||||
|
@ -761,11 +766,11 @@ void
|
|||
_PyGILState_Init(PyInterpreterState *i, PyThreadState *t)
|
||||
{
|
||||
assert(i && t); /* must init with valid states */
|
||||
_PyRuntime.gilstate.autoTLSkey = PyThread_create_key();
|
||||
if (_PyRuntime.gilstate.autoTLSkey == -1)
|
||||
Py_FatalError("Could not allocate TLS entry");
|
||||
if (PyThread_tss_create(&_PyRuntime.gilstate.autoTSSkey) != 0) {
|
||||
Py_FatalError("Could not allocate TSS entry");
|
||||
}
|
||||
_PyRuntime.gilstate.autoInterpreterState = i;
|
||||
assert(PyThread_get_key_value(_PyRuntime.gilstate.autoTLSkey) == NULL);
|
||||
assert(PyThread_tss_get(&_PyRuntime.gilstate.autoTSSkey) == NULL);
|
||||
assert(t->gilstate_counter == 0);
|
||||
|
||||
_PyGILState_NoteThreadState(t);
|
||||
|
@ -780,14 +785,13 @@ _PyGILState_GetInterpreterStateUnsafe(void)
|
|||
void
|
||||
_PyGILState_Fini(void)
|
||||
{
|
||||
PyThread_delete_key(_PyRuntime.gilstate.autoTLSkey);
|
||||
_PyRuntime.gilstate.autoTLSkey = -1;
|
||||
PyThread_tss_delete(&_PyRuntime.gilstate.autoTSSkey);
|
||||
_PyRuntime.gilstate.autoInterpreterState = NULL;
|
||||
}
|
||||
|
||||
/* Reset the TLS key - called by PyOS_AfterFork_Child().
|
||||
/* Reset the TSS key - called by PyOS_AfterFork_Child().
|
||||
* This should not be necessary, but some - buggy - pthread implementations
|
||||
* don't reset TLS upon fork(), see issue #10517.
|
||||
* don't reset TSS upon fork(), see issue #10517.
|
||||
*/
|
||||
void
|
||||
_PyGILState_Reinit(void)
|
||||
|
@ -796,15 +800,18 @@ _PyGILState_Reinit(void)
|
|||
if (_PyRuntime.interpreters.mutex == NULL)
|
||||
Py_FatalError("Can't initialize threads for interpreter");
|
||||
PyThreadState *tstate = PyGILState_GetThisThreadState();
|
||||
PyThread_delete_key(_PyRuntime.gilstate.autoTLSkey);
|
||||
if ((_PyRuntime.gilstate.autoTLSkey = PyThread_create_key()) == -1)
|
||||
Py_FatalError("Could not allocate TLS entry");
|
||||
PyThread_tss_delete(&_PyRuntime.gilstate.autoTSSkey);
|
||||
if (PyThread_tss_create(&_PyRuntime.gilstate.autoTSSkey) != 0) {
|
||||
Py_FatalError("Could not allocate TSS entry");
|
||||
}
|
||||
|
||||
/* If the thread had an associated auto thread state, reassociate it with
|
||||
* the new key. */
|
||||
if (tstate && PyThread_set_key_value(_PyRuntime.gilstate.autoTLSkey,
|
||||
(void *)tstate) < 0)
|
||||
Py_FatalError("Couldn't create autoTLSkey mapping");
|
||||
if (tstate &&
|
||||
PyThread_tss_set(&_PyRuntime.gilstate.autoTSSkey, (void *)tstate) != 0)
|
||||
{
|
||||
Py_FatalError("Couldn't create autoTSSkey mapping");
|
||||
}
|
||||
}
|
||||
|
||||
/* When a thread state is created for a thread by some mechanism other than
|
||||
|
@ -815,13 +822,13 @@ _PyGILState_Reinit(void)
|
|||
static void
|
||||
_PyGILState_NoteThreadState(PyThreadState* tstate)
|
||||
{
|
||||
/* If autoTLSkey isn't initialized, this must be the very first
|
||||
/* If autoTSSkey isn't initialized, this must be the very first
|
||||
threadstate created in Py_Initialize(). Don't do anything for now
|
||||
(we'll be back here when _PyGILState_Init is called). */
|
||||
if (!_PyRuntime.gilstate.autoInterpreterState)
|
||||
return;
|
||||
|
||||
/* Stick the thread state for this thread in thread local storage.
|
||||
/* Stick the thread state for this thread in thread specific storage.
|
||||
|
||||
The only situation where you can legitimately have more than one
|
||||
thread state for an OS level thread is when there are multiple
|
||||
|
@ -833,12 +840,11 @@ _PyGILState_NoteThreadState(PyThreadState* tstate)
|
|||
The first thread state created for that given OS level thread will
|
||||
"win", which seems reasonable behaviour.
|
||||
*/
|
||||
if (PyThread_get_key_value(_PyRuntime.gilstate.autoTLSkey) == NULL) {
|
||||
if ((PyThread_set_key_value(_PyRuntime.gilstate.autoTLSkey,
|
||||
(void *)tstate)
|
||||
) < 0)
|
||||
if (PyThread_tss_get(&_PyRuntime.gilstate.autoTSSkey) == NULL) {
|
||||
if ((PyThread_tss_set(&_PyRuntime.gilstate.autoTSSkey, (void *)tstate)
|
||||
) != 0)
|
||||
{
|
||||
Py_FatalError("Couldn't create autoTLSkey mapping");
|
||||
Py_FatalError("Couldn't create autoTSSkey mapping");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -852,8 +858,7 @@ PyGILState_GetThisThreadState(void)
|
|||
{
|
||||
if (_PyRuntime.gilstate.autoInterpreterState == NULL)
|
||||
return NULL;
|
||||
return (PyThreadState *)PyThread_get_key_value(
|
||||
_PyRuntime.gilstate.autoTLSkey);
|
||||
return (PyThreadState *)PyThread_tss_get(&_PyRuntime.gilstate.autoTSSkey);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -864,8 +869,9 @@ PyGILState_Check(void)
|
|||
if (!_PyGILState_check_enabled)
|
||||
return 1;
|
||||
|
||||
if (_PyRuntime.gilstate.autoTLSkey == -1)
|
||||
if (!PyThread_tss_is_created(&_PyRuntime.gilstate.autoTSSkey)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
tstate = GET_TSTATE();
|
||||
if (tstate == NULL)
|
||||
|
@ -886,8 +892,7 @@ PyGILState_Ensure(void)
|
|||
*/
|
||||
/* Py_Initialize() hasn't been called! */
|
||||
assert(_PyRuntime.gilstate.autoInterpreterState);
|
||||
tcur = (PyThreadState *)PyThread_get_key_value(
|
||||
_PyRuntime.gilstate.autoTLSkey);
|
||||
tcur = (PyThreadState *)PyThread_tss_get(&_PyRuntime.gilstate.autoTSSkey);
|
||||
if (tcur == NULL) {
|
||||
/* At startup, Python has no concrete GIL. If PyGILState_Ensure() is
|
||||
called from a new thread for the first time, we need the create the
|
||||
|
@ -919,8 +924,8 @@ PyGILState_Ensure(void)
|
|||
void
|
||||
PyGILState_Release(PyGILState_STATE oldstate)
|
||||
{
|
||||
PyThreadState *tcur = (PyThreadState *)PyThread_get_key_value(
|
||||
_PyRuntime.gilstate.autoTLSkey);
|
||||
PyThreadState *tcur = (PyThreadState *)PyThread_tss_get(
|
||||
&_PyRuntime.gilstate.autoTSSkey);
|
||||
if (tcur == NULL)
|
||||
Py_FatalError("auto-releasing thread-state, "
|
||||
"but no thread-state for this thread");
|
||||
|
|
|
@ -84,7 +84,7 @@ PyThread_init_thread(void)
|
|||
# define PYTHREAD_NAME "nt"
|
||||
# include "thread_nt.h"
|
||||
#else
|
||||
# error "Require native thread feature. See https://bugs.python.org/issue30832"
|
||||
# error "Require native threads. See https://bugs.python.org/issue31370"
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -111,41 +111,37 @@ PyThread_set_stacksize(size_t size)
|
|||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
Per-thread data ("key") support.
|
||||
/* Thread Specific Storage (TSS) API
|
||||
|
||||
Use PyThread_create_key() to create a new key. This is typically shared
|
||||
across threads.
|
||||
Cross-platform components of TSS API implementation.
|
||||
*/
|
||||
|
||||
Use PyThread_set_key_value(thekey, value) to associate void* value with
|
||||
thekey in the current thread. Each thread has a distinct mapping of thekey
|
||||
to a void* value. Caution: if the current thread already has a mapping
|
||||
for thekey, value is ignored.
|
||||
Py_tss_t *
|
||||
PyThread_tss_alloc(void)
|
||||
{
|
||||
Py_tss_t *new_key = (Py_tss_t *)PyMem_RawMalloc(sizeof(Py_tss_t));
|
||||
if (new_key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
new_key->_is_initialized = 0;
|
||||
return new_key;
|
||||
}
|
||||
|
||||
Use PyThread_get_key_value(thekey) to retrieve the void* value associated
|
||||
with thekey in the current thread. This returns NULL if no value is
|
||||
associated with thekey in the current thread.
|
||||
void
|
||||
PyThread_tss_free(Py_tss_t *key)
|
||||
{
|
||||
if (key != NULL) {
|
||||
PyThread_tss_delete(key);
|
||||
PyMem_RawFree((void *)key);
|
||||
}
|
||||
}
|
||||
|
||||
Use PyThread_delete_key_value(thekey) to forget the current thread's associated
|
||||
value for thekey. PyThread_delete_key(thekey) forgets the values associated
|
||||
with thekey across *all* threads.
|
||||
|
||||
While some of these functions have error-return values, none set any
|
||||
Python exception.
|
||||
|
||||
None of the functions does memory management on behalf of the void* values.
|
||||
You need to allocate and deallocate them yourself. If the void* values
|
||||
happen to be PyObject*, these functions don't do refcount operations on
|
||||
them either.
|
||||
|
||||
The GIL does not need to be held when calling these functions; they supply
|
||||
their own locking. This isn't true of PyThread_create_key(), though (see
|
||||
next paragraph).
|
||||
|
||||
There's a hidden assumption that PyThread_create_key() will be called before
|
||||
any of the other functions are called. There's also a hidden assumption
|
||||
that calls to PyThread_create_key() are serialized externally.
|
||||
------------------------------------------------------------------------ */
|
||||
int
|
||||
PyThread_tss_is_created(Py_tss_t *key)
|
||||
{
|
||||
assert(key != NULL);
|
||||
return key->_is_initialized;
|
||||
}
|
||||
|
||||
|
||||
PyDoc_STRVAR(threadinfo__doc__,
|
||||
|
|
|
@ -349,10 +349,15 @@ _pythread_nt_set_stacksize(size_t size)
|
|||
#define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x)
|
||||
|
||||
|
||||
/* Thread Local Storage (TLS) API
|
||||
|
||||
This API is DEPRECATED since Python 3.7. See PEP 539 for details.
|
||||
*/
|
||||
|
||||
int
|
||||
PyThread_create_key(void)
|
||||
{
|
||||
DWORD result= TlsAlloc();
|
||||
DWORD result = TlsAlloc();
|
||||
if (result == TLS_OUT_OF_INDEXES)
|
||||
return -1;
|
||||
return (int)result;
|
||||
|
@ -367,12 +372,8 @@ PyThread_delete_key(int key)
|
|||
int
|
||||
PyThread_set_key_value(int key, void *value)
|
||||
{
|
||||
BOOL ok;
|
||||
|
||||
ok = TlsSetValue(key, value);
|
||||
if (!ok)
|
||||
return -1;
|
||||
return 0;
|
||||
BOOL ok = TlsSetValue(key, value);
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
void *
|
||||
|
@ -399,9 +400,74 @@ PyThread_delete_key_value(int key)
|
|||
TlsSetValue(key, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* reinitialization of TLS is not necessary after fork when using
|
||||
* the native TLS functions. And forking isn't supported on Windows either.
|
||||
*/
|
||||
void
|
||||
PyThread_ReInitTLS(void)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* Thread Specific Storage (TSS) API
|
||||
|
||||
Platform-specific components of TSS API implementation.
|
||||
*/
|
||||
|
||||
int
|
||||
PyThread_tss_create(Py_tss_t *key)
|
||||
{
|
||||
assert(key != NULL);
|
||||
/* If the key has been created, function is silently skipped. */
|
||||
if (key->_is_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD result = TlsAlloc();
|
||||
if (result == TLS_OUT_OF_INDEXES) {
|
||||
return -1;
|
||||
}
|
||||
/* In Windows, platform-specific key type is DWORD. */
|
||||
key->_key = result;
|
||||
key->_is_initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
PyThread_tss_delete(Py_tss_t *key)
|
||||
{
|
||||
assert(key != NULL);
|
||||
/* If the key has not been created, function is silently skipped. */
|
||||
if (!key->_is_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
TlsFree(key->_key);
|
||||
key->_key = TLS_OUT_OF_INDEXES;
|
||||
key->_is_initialized = 0;
|
||||
}
|
||||
|
||||
int
|
||||
PyThread_tss_set(Py_tss_t *key, void *value)
|
||||
{
|
||||
assert(key != NULL);
|
||||
BOOL ok = TlsSetValue(key->_key, value);
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
void *
|
||||
PyThread_tss_get(Py_tss_t *key)
|
||||
{
|
||||
assert(key != NULL);
|
||||
/* because TSS is used in the Py_END_ALLOW_THREAD macro,
|
||||
* it is necessary to preserve the windows error state, because
|
||||
* it is assumed to be preserved across the call to the macro.
|
||||
* Ideally, the macro should be fixed, but it is simpler to
|
||||
* do it here.
|
||||
*/
|
||||
DWORD error = GetLastError();
|
||||
void *result = TlsGetValue(key->_key);
|
||||
SetLastError(error);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -589,9 +589,25 @@ _pythread_pthread_set_stacksize(size_t size)
|
|||
#define THREAD_SET_STACKSIZE(x) _pythread_pthread_set_stacksize(x)
|
||||
|
||||
|
||||
/* Thread Local Storage (TLS) API
|
||||
|
||||
This API is DEPRECATED since Python 3.7. See PEP 539 for details.
|
||||
*/
|
||||
|
||||
/* Issue #25658: On platforms where native TLS key is defined in a way that
|
||||
cannot be safely cast to int, PyThread_create_key returns immediately a
|
||||
failure status and other TLS functions all are no-ops. This indicates
|
||||
clearly that the old API is not supported on platforms where it cannot be
|
||||
used reliably, and that no effort will be made to add such support.
|
||||
|
||||
Note: PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT will be unnecessary after
|
||||
removing this API.
|
||||
*/
|
||||
|
||||
int
|
||||
PyThread_create_key(void)
|
||||
{
|
||||
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
|
||||
pthread_key_t key;
|
||||
int fail = pthread_key_create(&key, NULL);
|
||||
if (fail)
|
||||
|
@ -603,34 +619,102 @@ PyThread_create_key(void)
|
|||
return -1;
|
||||
}
|
||||
return (int)key;
|
||||
#else
|
||||
return -1; /* never return valid key value. */
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
PyThread_delete_key(int key)
|
||||
{
|
||||
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
|
||||
pthread_key_delete(key);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
PyThread_delete_key_value(int key)
|
||||
{
|
||||
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
|
||||
pthread_setspecific(key, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
PyThread_set_key_value(int key, void *value)
|
||||
{
|
||||
int fail;
|
||||
fail = pthread_setspecific(key, value);
|
||||
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
|
||||
int fail = pthread_setspecific(key, value);
|
||||
return fail ? -1 : 0;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *
|
||||
PyThread_get_key_value(int key)
|
||||
{
|
||||
#ifdef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT
|
||||
return pthread_getspecific(key);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PyThread_ReInitTLS(void)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* Thread Specific Storage (TSS) API
|
||||
|
||||
Platform-specific components of TSS API implementation.
|
||||
*/
|
||||
|
||||
int
|
||||
PyThread_tss_create(Py_tss_t *key)
|
||||
{
|
||||
assert(key != NULL);
|
||||
/* If the key has been created, function is silently skipped. */
|
||||
if (key->_is_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fail = pthread_key_create(&(key->_key), NULL);
|
||||
if (fail) {
|
||||
return -1;
|
||||
}
|
||||
key->_is_initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
PyThread_tss_delete(Py_tss_t *key)
|
||||
{
|
||||
assert(key != NULL);
|
||||
/* If the key has not been created, function is silently skipped. */
|
||||
if (!key->_is_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_key_delete(key->_key);
|
||||
/* pthread has not provided the defined invalid value for the key. */
|
||||
key->_is_initialized = 0;
|
||||
}
|
||||
|
||||
int
|
||||
PyThread_tss_set(Py_tss_t *key, void *value)
|
||||
{
|
||||
assert(key != NULL);
|
||||
int fail = pthread_setspecific(key->_key, value);
|
||||
return fail ? -1 : 0;
|
||||
}
|
||||
|
||||
void *
|
||||
PyThread_tss_get(Py_tss_t *key)
|
||||
{
|
||||
assert(key != NULL);
|
||||
return pthread_getspecific(key->_key);
|
||||
}
|
||||
|
|
|
@ -760,7 +760,7 @@ _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
|
|||
|
||||
PyThreadState_Get() doesn't give the state of the thread that caused
|
||||
the fault if the thread released the GIL, and so this function
|
||||
cannot be used. Read the thread local storage (TLS) instead: call
|
||||
cannot be used. Read the thread specific storage (TSS) instead: call
|
||||
PyGILState_GetThisThreadState(). */
|
||||
current_tstate = PyGILState_GetThisThreadState();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue