diff -r c47a72627f0c Include/pythread.h --- a/Include/pythread.h Sun Jan 22 14:41:42 2017 +0800 +++ b/Include/pythread.h Mon Jan 23 00:06:32 2017 +0900 @@ -2,6 +2,8 @@ #ifndef Py_PYTHREAD_H #define Py_PYTHREAD_H +#include /* necessary for TSS key */ + typedef void *PyThread_type_lock; typedef void *PyThread_type_sema; @@ -25,8 +27,8 @@ PyAPI_FUNC(PyThread_type_lock) PyThread_allocate_lock(void); PyAPI_FUNC(void) PyThread_free_lock(PyThread_type_lock); PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int); -#define WAIT_LOCK 1 -#define NOWAIT_LOCK 0 +#define WAIT_LOCK 1 +#define NOWAIT_LOCK 0 /* PY_TIMEOUT_T is the integral type used to specify timeouts when waiting on a lock (see PyThread_acquire_lock_timed() below). @@ -73,15 +75,66 @@ PyAPI_FUNC(PyObject*) PyThread_GetInfo(void); #endif -/* Thread Local Storage (TLS) API */ -PyAPI_FUNC(int) PyThread_create_key(void); -PyAPI_FUNC(void) PyThread_delete_key(int); -PyAPI_FUNC(int) PyThread_set_key_value(int, void *); -PyAPI_FUNC(void *) PyThread_get_key_value(int); -PyAPI_FUNC(void) PyThread_delete_key_value(int key); +/* Thread Local Storage (TLS) API + TLS API is DEPRECATED. Use Thread Specific Storage API. +*/ +PyAPI_FUNC(int) PyThread_create_key(void) Py_DEPRECATED(3.7); +PyAPI_FUNC(void) PyThread_delete_key(int key) Py_DEPRECATED(3.7); +PyAPI_FUNC(int) PyThread_set_key_value(int key, void *value) Py_DEPRECATED(3.7); +PyAPI_FUNC(void *) PyThread_get_key_value(int key) Py_DEPRECATED(3.7); +PyAPI_FUNC(void) PyThread_delete_key_value(int key) Py_DEPRECATED(3.7); /* Cleanup after a fork */ -PyAPI_FUNC(void) PyThread_ReInitTLS(void); +PyAPI_FUNC(void) PyThread_ReInitTLS(void) Py_DEPRECATED(3.7); + +/* Thread Specific Storage (TSS) API + + POSIX hasn't defined that pthread_key_t is compatible with int + (for details, see PEP 539). Therefore, TSS API uses opaque type to cover + the key details. +*/ + +#if defined(_POSIX_THREADS) +# define NATIVE_TLS_KEY_T pthread_key_t +#elif defined(NT_THREADS) +# define NATIVE_TLS_KEY_T DWORD +#else /* For the platform that has not supplied native TLS */ +# define NATIVE_TLS_KEY_T int +#endif + +/* Py_tss_t is opaque type and you *must not* directly read and write. + When you'd check whether the key is created, use PyThread_tss_is_created. +*/ +typedef struct { + bool _is_initialized; + NATIVE_TLS_KEY_T _key; +} Py_tss_t; + +#undef NATIVE_TLS_KEY_T + +static inline bool +PyThread_tss_is_created(Py_tss_t key) +{ + return key._is_initialized; +} + +/* Py_tss_NEEDS_INIT is the defined invalid value, and you *must* initialize + the Py_tss_t variable by this value to use TSS API. + + For example: + static Py_tss_t thekey = Py_tss_NEEDS_INIT; + int fail = PyThread_tss_create(&thekey); +*/ +#define Py_tss_NEEDS_INIT {._is_initialized = false} + +PyAPI_FUNC(int) PyThread_tss_create(Py_tss_t *key); +PyAPI_FUNC(void) PyThread_tss_delete(Py_tss_t *key); +PyAPI_FUNC(int) PyThread_tss_set(Py_tss_t key, void *value); +PyAPI_FUNC(void *) PyThread_tss_get(Py_tss_t key); +PyAPI_FUNC(void) PyThread_tss_delete_value(Py_tss_t key); + +/* Cleanup after a fork */ +PyAPI_FUNC(void) PyThread_ReInitTSS(void); #ifdef __cplusplus } diff -r c47a72627f0c Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Sun Jan 22 14:41:42 2017 +0800 +++ b/Modules/_testcapimodule.c Mon Jan 23 00:06:32 2017 +0900 @@ -4039,6 +4039,49 @@ return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)version); } +#ifdef WITH_THREAD +static PyObject * +test_pythread_tss_key_state(PyObject *self, PyObject *args) +{ + Py_tss_t tss_key = Py_tss_NEEDS_INIT; + if (PyThread_tss_is_created(tss_key)) { + return raiseTestError("test_pythread_tss_key_state", + "tss key hasn't been non created state at " + "initialization"); + } + if (PyThread_tss_create(&tss_key) != 0) { + PyErr_SetString(PyExc_RuntimeError, "PyThread_tss_create failed"); + return NULL; + } + if (!PyThread_tss_is_created(tss_key)) { + return raiseTestError("test_pythread_tss_key_state", + "PyThread_tss_create succeeded, " + "but tss key didn't make created state"); + } + if (PyThread_tss_create(&tss_key) != 0) { + return raiseTestError("test_pythread_tss_key_state", + "PyThread_tss_create didn't succeed with " + "created key"); + } +#define CHECK_TSS_API(expr) \ + (void)(expr); \ + if (!PyThread_tss_is_created(tss_key)) { \ + return raiseTestError("test_pythread_tss_key_state", \ + "tss key state that has been created " \ + "wasn't kept after calling " #expr); } + CHECK_TSS_API(PyThread_tss_set(tss_key, NULL)); + CHECK_TSS_API(PyThread_tss_get(tss_key)); + CHECK_TSS_API(PyThread_tss_delete_value(tss_key)); +#undef CHECK_TSS_API + PyThread_tss_delete(&tss_key); + if (PyThread_tss_is_created(tss_key)) { + return raiseTestError("test_pythread_tss_key_state", + "PyThread_tss_delete called, " + "but tss key didn't make non created state"); + } + Py_RETURN_NONE; +} +#endif static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, @@ -4244,6 +4287,9 @@ {"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS}, {"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS}, {"dict_get_version", dict_get_version, METH_VARARGS}, +#ifdef WITH_THREAD + {"test_pythread_tss_key_state", test_pythread_tss_key_state, METH_VARARGS}, +#endif {NULL, NULL} /* sentinel */ }; diff -r c47a72627f0c Modules/_tracemalloc.c --- a/Modules/_tracemalloc.c Sun Jan 22 14:41:42 2017 +0800 +++ b/Modules/_tracemalloc.c Mon Jan 23 00:06:32 2017 +0900 @@ -165,7 +165,7 @@ # error "need native thread local storage (TLS)" #endif -static int tracemalloc_reentrant_key = -1; +static Py_tss_t tracemalloc_reentrant_key = Py_tss_NEEDS_INIT; /* Any non-NULL pointer can be used */ #define REENTRANT Py_True @@ -175,8 +175,8 @@ { void *ptr; - assert(tracemalloc_reentrant_key != -1); - ptr = PyThread_get_key_value(tracemalloc_reentrant_key); + assert(PyThread_tss_is_created(tracemalloc_reentrant_key)); + ptr = PyThread_tss_get(tracemalloc_reentrant_key); if (ptr != NULL) { assert(ptr == REENTRANT); return 1; @@ -189,15 +189,15 @@ set_reentrant(int reentrant) { assert(reentrant == 0 || reentrant == 1); - assert(tracemalloc_reentrant_key != -1); + assert(PyThread_tss_is_created(tracemalloc_reentrant_key)); if (reentrant) { assert(!get_reentrant()); - PyThread_set_key_value(tracemalloc_reentrant_key, REENTRANT); + PyThread_tss_set(tracemalloc_reentrant_key, REENTRANT); } else { assert(get_reentrant()); - PyThread_set_key_value(tracemalloc_reentrant_key, NULL); + PyThread_tss_set(tracemalloc_reentrant_key, NULL); } } @@ -987,8 +987,7 @@ PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &allocators.raw); #ifdef REENTRANT_THREADLOCAL - tracemalloc_reentrant_key = PyThread_create_key(); - if (tracemalloc_reentrant_key == -1) { + if (PyThread_tss_create(&tracemalloc_reentrant_key) != 0) { #ifdef MS_WINDOWS PyErr_SetFromWindowsErr(0); #else @@ -1073,8 +1072,7 @@ #endif #ifdef REENTRANT_THREADLOCAL - PyThread_delete_key(tracemalloc_reentrant_key); - tracemalloc_reentrant_key = -1; + PyThread_tss_delete(&tracemalloc_reentrant_key); #endif Py_XDECREF(unknown_filename); diff -r c47a72627f0c Modules/faulthandler.c --- a/Modules/faulthandler.c Sun Jan 22 14:41:42 2017 +0800 +++ b/Modules/faulthandler.c Mon Jan 23 00:06:32 2017 +0900 @@ -231,7 +231,7 @@ 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 + used. Read the thread specific storage (TSS) instead: call PyGILState_GetThisThreadState(). */ tstate = PyGILState_GetThisThreadState(); #else diff -r c47a72627f0c Modules/signalmodule.c --- a/Modules/signalmodule.c Sun Jan 22 14:41:42 2017 +0800 +++ b/Modules/signalmodule.c Mon Jan 23 00:06:32 2017 +0900 @@ -1605,9 +1605,9 @@ * the interpreter had an opportunity to call the handlers. issue9535. */ _clear_pending_signals(); #ifdef WITH_THREAD - /* PyThread_ReInitTLS() must be called early, to make sure that the TLS API + /* PyThread_ReInitTSS() must be called early, to make sure that the TSS API * can be called safely. */ - PyThread_ReInitTLS(); + PyThread_ReInitTSS(); _PyGILState_Reinit(); PyEval_ReInitThreads(); main_thread = PyThread_get_thread_ident(); diff -r c47a72627f0c Python/pystate.c --- a/Python/pystate.c Sun Jan 22 14:41:42 2017 +0800 +++ b/Python/pystate.c Mon Jan 23 00:06:32 2017 +0900 @@ -47,7 +47,7 @@ GILState implementation */ static PyInterpreterState *autoInterpreterState = NULL; -static int autoTLSkey = -1; +static Py_tss_t autoTSSkey = Py_tss_NEEDS_INIT; #else #define HEAD_INIT() /* Nothing */ #define HEAD_LOCK() /* Nothing */ @@ -444,8 +444,8 @@ if (tstate == GET_TSTATE()) Py_FatalError("PyThreadState_Delete: tstate is still current"); #ifdef WITH_THREAD - if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) - PyThread_delete_key_value(autoTLSkey); + if (autoInterpreterState && PyThread_tss_get(autoTSSkey) == tstate) + PyThread_tss_delete_value(autoTSSkey); #endif /* WITH_THREAD */ tstate_delete_common(tstate); } @@ -460,8 +460,8 @@ Py_FatalError( "PyThreadState_DeleteCurrent: no current tstate"); tstate_delete_common(tstate); - if (autoInterpreterState && PyThread_get_key_value(autoTLSkey) == tstate) - PyThread_delete_key_value(autoTLSkey); + if (autoInterpreterState && PyThread_tss_get(autoTSSkey) == tstate) + PyThread_tss_delete_value(autoTSSkey); SET_TSTATE(NULL); PyEval_ReleaseLock(); } @@ -712,11 +712,10 @@ _PyGILState_Init(PyInterpreterState *i, PyThreadState *t) { assert(i && t); /* must init with valid states */ - autoTLSkey = PyThread_create_key(); - if (autoTLSkey == -1) - Py_FatalError("Could not allocate TLS entry"); + if (PyThread_tss_create(&autoTSSkey) != 0) + Py_FatalError("Could not allocate TSS entry"); autoInterpreterState = i; - assert(PyThread_get_key_value(autoTLSkey) == NULL); + assert(PyThread_tss_get(autoTSSkey) == NULL); assert(t->gilstate_counter == 0); _PyGILState_NoteThreadState(t); @@ -731,27 +730,26 @@ void _PyGILState_Fini(void) { - PyThread_delete_key(autoTLSkey); - autoTLSkey = -1; + PyThread_tss_delete(&autoTSSkey); autoInterpreterState = NULL; } -/* Reset the TLS key - called by PyOS_AfterFork(). +/* Reset the TSS key - called by PyOS_AfterFork(). * 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) { PyThreadState *tstate = PyGILState_GetThisThreadState(); - PyThread_delete_key(autoTLSkey); - if ((autoTLSkey = PyThread_create_key()) == -1) - Py_FatalError("Could not allocate TLS entry"); + PyThread_tss_delete(&autoTSSkey); + if (PyThread_tss_create(&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(autoTLSkey, (void *)tstate) < 0) - Py_FatalError("Couldn't create autoTLSkey mapping"); + if (tstate && PyThread_tss_set(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 @@ -762,13 +760,13 @@ 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 (!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 @@ -780,9 +778,9 @@ The first thread state created for that given OS level thread will "win", which seems reasonable behaviour. */ - if (PyThread_get_key_value(autoTLSkey) == NULL) { - if (PyThread_set_key_value(autoTLSkey, (void *)tstate) < 0) - Py_FatalError("Couldn't create autoTLSkey mapping"); + if (PyThread_tss_get(autoTSSkey) == NULL) { + if (PyThread_tss_set(autoTSSkey, (void *)tstate) != 0) + Py_FatalError("Couldn't create autoTSSkey mapping"); } /* PyGILState_Release must not try to delete this thread state. */ @@ -795,7 +793,7 @@ { if (autoInterpreterState == NULL) return NULL; - return (PyThreadState *)PyThread_get_key_value(autoTLSkey); + return (PyThreadState *)PyThread_tss_get(autoTSSkey); } int @@ -806,7 +804,7 @@ if (!_PyGILState_check_enabled) return 1; - if (autoTLSkey == -1) + if (!PyThread_tss_is_created(autoTSSkey)) return 1; tstate = GET_TSTATE(); @@ -827,7 +825,7 @@ called Py_Initialize() and usually PyEval_InitThreads(). */ assert(autoInterpreterState); /* Py_Initialize() hasn't been called! */ - tcur = (PyThreadState *)PyThread_get_key_value(autoTLSkey); + tcur = (PyThreadState *)PyThread_tss_get(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 @@ -859,8 +857,7 @@ void PyGILState_Release(PyGILState_STATE oldstate) { - PyThreadState *tcur = (PyThreadState *)PyThread_get_key_value( - autoTLSkey); + PyThreadState *tcur = (PyThreadState *)PyThread_tss_get(autoTSSkey); if (tcur == NULL) Py_FatalError("auto-releasing thread-state, " "but no thread-state for this thread"); diff -r c47a72627f0c Python/thread.c --- a/Python/thread.c Sun Jan 22 14:41:42 2017 +0800 +++ b/Python/thread.c Mon Jan 23 00:06:32 2017 +0900 @@ -20,6 +20,7 @@ #include #endif +#include /* necessary for TSS key */ #include #include "pythread.h" @@ -125,25 +126,25 @@ TLS implementation, provide our own. This code stolen from "thread_sgi.h", where it was the only - implementation of an existing Python TLS API. + implementation of a Python TLS API that has been deprecated. */ /* ------------------------------------------------------------------------ Per-thread data ("key") support. -Use PyThread_create_key() to create a new key. This is typically shared +Use PyThread_tss_create(&thekey) to create a new key. This is typically shared across threads. -Use PyThread_set_key_value(thekey, value) to associate void* value with +Use PyThread_tss_set(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 +Use PyThread_tss_get(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 +Use PyThread_tss_delete_value(thekey) to forget the current thread's associated +value for thekey. PyThread_tss_delete(&thekey) forgets the values associated with thekey across *all* threads. While some of these functions have error-return values, none set any @@ -155,12 +156,12 @@ 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 +their own locking. This isn't true of PyThread_tss_create(), though (see next paragraph). -There's a hidden assumption that PyThread_create_key() will be called before +There's a hidden assumption that PyThread_tss_create() 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. +that calls to PyThread_tss_create() are serialized externally. ------------------------------------------------------------------------ */ /* A singly-linked list of struct key objects remembers all the key->value @@ -181,7 +182,7 @@ static struct key *keyhead = NULL; static PyThread_type_lock keymutex = NULL; -static int nkeys = 0; /* PyThread_create_key() hands out nkeys+1 next */ +static int nkeys = 0; /* PyThread_tss_create() hands out nkeys+1 next */ /* Internal helper. * If the current thread has a mapping for key, the appropriate struct key* @@ -200,7 +201,7 @@ * another thread to mutate the list, via key deletion, concurrent with * find_key() crawling over the list. Hilarity ensued. For example, when * the for-loop here does "p = p->next", p could end up pointing at a - * record that PyThread_delete_key_value() was concurrently free()'ing. + * record that PyThread_tss_delete_value() was concurrently free()'ing. * That could lead to anything, from failing to find a key that exists, to * segfaults. Now we lock the whole routine. */ @@ -225,10 +226,10 @@ * in a tight loop with the lock held. A similar check is done * in pystate.c tstate_delete_common(). */ if (p == prev_p) - Py_FatalError("tls find_key: small circular list(!)"); + Py_FatalError("tss find_key: small circular list(!)"); prev_p = p; if (p->next == keyhead) - Py_FatalError("tls find_key: circular list(!)"); + Py_FatalError("tss find_key: circular list(!)"); } if (!set_value && value == NULL) { assert(p == NULL); @@ -247,31 +248,97 @@ return p; } -/* Return a new key. This must be called before any other functions in + +/* Thread Local Storage (TLS) API, DEPRECATED since Python 3.7 + + In CPython self implementation, TLS API has been changed to call TSS API. +*/ + +int +PyThread_create_key(void) +{ + Py_tss_t proxy = Py_tss_NEEDS_INIT; + if (PyThread_tss_create(&proxy) != 0) + return -1; + /* In CPython self implementation, platform-specific key type is int. */ + return proxy._key; +} + +void +PyThread_delete_key(int key) +{ + Py_tss_t proxy = {._is_initialized = true, ._key = key}; + PyThread_tss_delete(&proxy); +} + +int +PyThread_set_key_value(int key, void *value) +{ + Py_tss_t proxy = {._is_initialized = true, ._key = key}; + return PyThread_tss_set(proxy, value); +} + +void * +PyThread_get_key_value(int key) +{ + Py_tss_t proxy = {._is_initialized = true, ._key = key}; + return PyThread_tss_get(proxy); +} + +void +PyThread_delete_key_value(int key) +{ + Py_tss_t proxy = {._is_initialized = true, ._key = key}; + PyThread_tss_delete_value(proxy); +} + + +void +PyThread_ReInitTLS(void) +{ + PyThread_ReInitTSS(); +} + + +/* Thread Specific Storage (TSS) API */ + +/* Assign a new key. This must be called before any other functions in * this family, and callers must arrange to serialize calls to this * function. No violations are detected. */ int -PyThread_create_key(void) +PyThread_tss_create(Py_tss_t *key) { /* All parts of this function are wrong if it's called by multiple * threads simultaneously. */ + assert(key != NULL); + /* If the key has been created, function is silently skipped. */ + if (key->_is_initialized) + return 0; + if (keymutex == NULL) keymutex = PyThread_allocate_lock(); - return ++nkeys; + key->_key = ++nkeys; + key->_is_initialized = true; + return 0; } /* Forget the associations for key across *all* threads. */ void -PyThread_delete_key(int key) +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; + struct key *p, **q; PyThread_acquire_lock(keymutex, 1); q = &keyhead; while ((p = *q) != NULL) { - if (p->key == key) { + if (p->key == key->_key) { *q = p->next; PyMem_RawFree((void *)p); /* NB This does *not* free p->value! */ @@ -279,15 +346,16 @@ else q = &p->next; } + key->_key = -1; + key->_is_initialized = false; PyThread_release_lock(keymutex); } int -PyThread_set_key_value(int key, void *value) +PyThread_tss_set(Py_tss_t key, void *value) { - struct key *p; + struct key *p = find_key(1, key._key, value); - p = find_key(1, key, value); if (p == NULL) return -1; else @@ -298,9 +366,9 @@ * if the current thread doesn't have an association for key. */ void * -PyThread_get_key_value(int key) +PyThread_tss_get(Py_tss_t key) { - struct key *p = find_key(0, key, NULL); + struct key *p = find_key(0, key._key, NULL); if (p == NULL) return NULL; @@ -310,7 +378,7 @@ /* Forget the current thread's association for key, if any. */ void -PyThread_delete_key_value(int key) +PyThread_tss_delete_value(Py_tss_t key) { long id = PyThread_get_thread_ident(); struct key *p, **q; @@ -318,7 +386,7 @@ PyThread_acquire_lock(keymutex, 1); q = &keyhead; while ((p = *q) != NULL) { - if (p->key == key && p->id == id) { + if (p->key == key._key && p->id == id) { *q = p->next; PyMem_RawFree((void *)p); /* NB This does *not* free p->value! */ @@ -330,13 +398,14 @@ PyThread_release_lock(keymutex); } + /* Forget everything not associated with the current thread id. * This function is called from PyOS_AfterFork(). It is necessary * because other thread ids which were in use at the time of the fork * may be reused for new threads created in the forked process. */ void -PyThread_ReInitTLS(void) +PyThread_ReInitTSS(void) { long id = PyThread_get_thread_ident(); struct key *p, **q; diff -r c47a72627f0c Python/thread_foobar.h --- a/Python/thread_foobar.h Sun Jan 22 14:41:42 2017 +0800 +++ b/Python/thread_foobar.h Mon Jan 23 00:06:32 2017 +0900 @@ -71,7 +71,7 @@ dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n", lock, microseconds, intr_flag)); dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n", - lock, microseconds, intr_flag, success)); + lock, microseconds, intr_flag, success)); return success; } @@ -85,6 +85,9 @@ #define Py_HAVE_NATIVE_TLS #ifdef Py_HAVE_NATIVE_TLS + +/* Thread Local Storage (TLS) API, DEPRECATED since Python 3.7 */ + int PyThread_create_key(void) { @@ -123,10 +126,74 @@ } + void PyThread_ReInitTLS(void) { } + +/* Thread Specific Storage (TSS) API */ + +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; + + Py_tss_t new_key; + /* A failure in this case returns -1 */ + if (!new_key._key) + return -1; + key->_key = new_key._key; + key->_is_initialized = true; + 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; + + key->_is_initialized = false; +} + +int +PyThread_tss_set(Py_tss_t key, void *value) +{ + int ok; + + /* A failure in this case returns -1 */ + if (!ok) + return -1; + return 0; +} + +void * +PyThread_tss_get(Py_tss_t key) +{ + void *result; + + return result; +} + +void +PyThread_tss_delete_value(Py_tss_t key) +{ + +} + + +void +PyThread_ReInitTSS(void) +{ + +} + #endif diff -r c47a72627f0c Python/thread_nt.h --- a/Python/thread_nt.h Sun Jan 22 14:41:42 2017 +0800 +++ b/Python/thread_nt.h Mon Jan 23 00:06:32 2017 +0900 @@ -8,6 +8,7 @@ #ifdef HAVE_PROCESS_H #include #endif +#include /* necessary for TSS key */ /* options */ #ifndef _PY_USE_CV_LOCKS @@ -352,10 +353,13 @@ #define Py_HAVE_NATIVE_TLS #ifdef Py_HAVE_NATIVE_TLS + +/* Thread Local Storage (TLS) API, DEPRECATED since Python 3.7 */ + int PyThread_create_key(void) { - DWORD result= TlsAlloc(); + DWORD result = TlsAlloc(); if (result == TLS_OUT_OF_INDEXES) return -1; return (int)result; @@ -370,12 +374,8 @@ 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 * @@ -402,6 +402,7 @@ 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. */ @@ -409,4 +410,76 @@ PyThread_ReInitTLS(void) {} + +/* Thread Specific Storage (TSS) API */ + +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 = true; + 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 = false; +} + +int +PyThread_tss_set(Py_tss_t key, void *value) +{ + BOOL ok = TlsSetValue(key._key, value); + return ok ? 0 : -1; +} + +void * +PyThread_tss_get(Py_tss_t key) +{ + /* 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; +} + +void +PyThread_tss_delete_value(Py_tss_t key) +{ + /* NULL is used as "key missing", and it is also the default + * given by TlsGetValue() if nothing has been set yet. + */ + TlsSetValue(key._key, NULL); +} + + +/* reinitialization of TSS is not necessary after fork when using + * the native TLS functions. And forking isn't supported on Windows either. + */ +void +PyThread_ReInitTSS(void) +{} + #endif diff -r c47a72627f0c Python/thread_pthread.h --- a/Python/thread_pthread.h Sun Jan 22 14:41:42 2017 +0800 +++ b/Python/thread_pthread.h Mon Jan 23 00:06:32 2017 +0900 @@ -1,6 +1,7 @@ /* Posix threads interface */ +#include /* necessary for TSS key */ #include #include #if defined(__APPLE__) || defined(HAVE_PTHREAD_DESTRUCTOR) @@ -603,9 +604,19 @@ #define Py_HAVE_NATIVE_TLS +/* Thread Local Storage (TLS) API, DEPRECATED since Python 3.7 + + Issue #25658: POSIX hasn't defined that pthread_key_t is compatible + with int. Note that if incompatible with int, PyThread_create_key + returns immediately a failure status and the other TLS functions + all are no-ops. Moreover, 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) @@ -617,34 +628,104 @@ 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 */ + +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 = true; + 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 = false; +} + +int +PyThread_tss_set(Py_tss_t key, void *value) +{ + int fail = pthread_setspecific(key._key, value); + return fail ? -1 : 0; +} + +void * +PyThread_tss_get(Py_tss_t key) +{ + return pthread_getspecific(key._key); +} + +void +PyThread_tss_delete_value(Py_tss_t key) +{ + pthread_setspecific(key._key, NULL); +} + + +void +PyThread_ReInitTSS(void) +{} diff -r c47a72627f0c Python/traceback.c --- a/Python/traceback.c Sun Jan 22 14:41:42 2017 +0800 +++ b/Python/traceback.c Mon Jan 23 00:06:32 2017 +0900 @@ -760,7 +760,7 @@ 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(); } diff -r c47a72627f0c configure --- a/configure Sun Jan 22 14:41:42 2017 +0800 +++ b/configure Mon Jan 23 00:06:32 2017 +0900 @@ -8981,6 +8981,75 @@ fi + +# Issue #25658: POSIX hasn't defined that pthread_key_t is compatible with int. +# This checking will be unnecessary after removing deprecated TLS API. +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of pthread_key_t" >&5 +$as_echo_n "checking size of pthread_key_t... " >&6; } +if ${ac_cv_sizeof_pthread_key_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_key_t))" "ac_cv_sizeof_pthread_key_t" "#include +"; then : + +else + if test "$ac_cv_type_pthread_key_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (pthread_key_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_pthread_key_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_pthread_key_t" >&5 +$as_echo "$ac_cv_sizeof_pthread_key_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_PTHREAD_KEY_T $ac_cv_sizeof_pthread_key_t +_ACEOF + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthread_key_t is compatible with int" >&5 +$as_echo_n "checking whether pthread_key_t is compatible with int... " >&6; } +if test "$ac_cv_sizeof_pthread_key_t" -eq "$ac_cv_sizeof_int" ; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +pthread_key_t k; k * 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_pthread_key_t_is_arithmetic_type=yes +else + ac_pthread_key_t_is_arithmetic_type=no + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pthread_key_t_is_arithmetic_type" >&5 +$as_echo "$ac_pthread_key_t_is_arithmetic_type" >&6; } + if test "$ac_pthread_key_t_is_arithmetic_type" = yes ; then + +$as_echo "#define PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT 1" >>confdefs.h + + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi CC="$ac_save_cc" diff -r c47a72627f0c configure.ac --- a/configure.ac Sun Jan 22 14:41:42 2017 +0800 +++ b/configure.ac Mon Jan 23 00:06:32 2017 +0900 @@ -2258,6 +2258,25 @@ #endif ]) fi + +# Issue #25658: POSIX hasn't defined that pthread_key_t is compatible with int. +# This checking will be unnecessary after removing deprecated TLS API. +AC_CHECK_SIZEOF(pthread_key_t, [], [[#include ]]) +AC_MSG_CHECKING(whether pthread_key_t is compatible with int) +if test "$ac_cv_sizeof_pthread_key_t" -eq "$ac_cv_sizeof_int" ; then + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include ]], [[pthread_key_t k; k * 1;]])], + [ac_pthread_key_t_is_arithmetic_type=yes], + [ac_pthread_key_t_is_arithmetic_type=no] + ) + AC_MSG_RESULT($ac_pthread_key_t_is_arithmetic_type) + if test "$ac_pthread_key_t_is_arithmetic_type" = yes ; then + AC_DEFINE(PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT, 1, + [Define if pthread_key_t is compatible with int.]) + fi +else + AC_MSG_RESULT(no) +fi CC="$ac_save_cc" AC_SUBST(OTHER_LIBTOOL_OPT) diff -r c47a72627f0c pyconfig.h.in --- a/pyconfig.h.in Sun Jan 22 14:41:42 2017 +0800 +++ b/pyconfig.h.in Mon Jan 23 00:06:32 2017 +0900 @@ -1241,6 +1241,9 @@ /* Define if POSIX semaphores aren't enabled on your system */ #undef POSIX_SEMAPHORES_NOT_ENABLED +/* Define if pthread_key_t is compatible with int. */ +#undef PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT + /* Defined if PTHREAD_SCOPE_SYSTEM supported. */ #undef PTHREAD_SYSTEM_SCHED_SUPPORTED @@ -1296,6 +1299,9 @@ /* The size of `pid_t', as computed by sizeof. */ #undef SIZEOF_PID_T +/* The size of `pthread_key_t', as computed by sizeof. */ +#undef SIZEOF_PTHREAD_KEY_T + /* The size of `pthread_t', as computed by sizeof. */ #undef SIZEOF_PTHREAD_T