diff options
author | Masayuki Yamamoto <ma3yuki.8mamo10@gmail.com> | 2017-10-06 10:41:34 (GMT) |
---|---|---|
committer | Nick Coghlan <ncoghlan@gmail.com> | 2017-10-06 10:41:34 (GMT) |
commit | 731e18901484c75b60167a06a0ba0719a6d4827d (patch) | |
tree | fc9b8afc6eb8453729c130a146b838228ab103c6 /Python | |
parent | b8ab9d3fc816f85f4d6dbef12b7414e6dc10e4dd (diff) | |
download | cpython-731e18901484c75b60167a06a0ba0719a6d4827d.zip cpython-731e18901484c75b60167a06a0ba0719a6d4827d.tar.gz cpython-731e18901484c75b60167a06a0ba0719a6d4827d.tar.bz2 |
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
Diffstat (limited to 'Python')
-rw-r--r-- | Python/pystate.c | 71 | ||||
-rw-r--r-- | Python/thread.c | 60 | ||||
-rw-r--r-- | Python/thread_nt.h | 82 | ||||
-rw-r--r-- | Python/thread_pthread.h | 90 | ||||
-rw-r--r-- | Python/traceback.c | 2 |
5 files changed, 228 insertions, 77 deletions
diff --git a/Python/pystate.c b/Python/pystate.c index 53c1236..3feae34 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -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"); diff --git a/Python/thread.c b/Python/thread.c index f742d05..7eac836 100644 --- a/Python/thread.c +++ b/Python/thread.c @@ -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. - -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. - -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. - -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. + Cross-platform components of TSS API implementation. +*/ -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. +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; +} -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). +void +PyThread_tss_free(Py_tss_t *key) +{ + if (key != NULL) { + PyThread_tss_delete(key); + PyMem_RawFree((void *)key); + } +} -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__, diff --git a/Python/thread_nt.h b/Python/thread_nt.h index 2f3a71b..bae8bcc 100644 --- a/Python/thread_nt.h +++ b/Python/thread_nt.h @@ -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; +} diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index 2dcd107..c5b7f32 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -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); +} diff --git a/Python/traceback.c b/Python/traceback.c index ba979aa..21b36b1 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -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(); } |