diff -r 1c247c8a9802 Include/pythread.h --- a/Include/pythread.h Mon Jan 09 13:12:09 2017 +0100 +++ b/Include/pythread.h Mon Jan 09 21:40:37 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,12 +75,60 @@ 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); + +/* 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 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_ReInitTLS(void); diff -r 1c247c8a9802 Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Mon Jan 09 13:12:09 2017 +0100 +++ b/Modules/_testcapimodule.c Mon Jan 09 21:40:37 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 1c247c8a9802 Modules/_tracemalloc.c --- a/Modules/_tracemalloc.c Mon Jan 09 13:12:09 2017 +0100 +++ b/Modules/_tracemalloc.c Mon Jan 09 21:40:37 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 1c247c8a9802 Python/pystate.c --- a/Python/pystate.c Mon Jan 09 13:12:09 2017 +0100 +++ b/Python/pystate.c Mon Jan 09 21:40:37 2017 +0900 @@ -47,7 +47,7 @@ GILState implementation */ static PyInterpreterState *autoInterpreterState = NULL; -static int autoTLSkey = -1; +static Py_tss_t autoTLSkey = 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(autoTLSkey) == tstate) + PyThread_tss_delete_value(autoTLSkey); #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(autoTLSkey) == tstate) + PyThread_tss_delete_value(autoTLSkey); 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) + if (PyThread_tss_create(&autoTLSkey) != 0) Py_FatalError("Could not allocate TLS entry"); autoInterpreterState = i; - assert(PyThread_get_key_value(autoTLSkey) == NULL); + assert(PyThread_tss_get(autoTLSkey) == NULL); assert(t->gilstate_counter == 0); _PyGILState_NoteThreadState(t); @@ -731,8 +730,7 @@ void _PyGILState_Fini(void) { - PyThread_delete_key(autoTLSkey); - autoTLSkey = -1; + PyThread_tss_delete(&autoTLSkey); autoInterpreterState = NULL; } @@ -744,13 +742,13 @@ _PyGILState_Reinit(void) { PyThreadState *tstate = PyGILState_GetThisThreadState(); - PyThread_delete_key(autoTLSkey); - if ((autoTLSkey = PyThread_create_key()) == -1) + PyThread_tss_delete(&autoTLSkey); + if (PyThread_tss_create(&autoTLSkey) != 0) Py_FatalError("Could not allocate TLS 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) + if (tstate && PyThread_tss_set(autoTLSkey, (void *)tstate) != 0) Py_FatalError("Couldn't create autoTLSkey mapping"); } @@ -780,8 +778,8 @@ 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) + if (PyThread_tss_get(autoTLSkey) == NULL) { + if (PyThread_tss_set(autoTLSkey, (void *)tstate) != 0) Py_FatalError("Couldn't create autoTLSkey mapping"); } @@ -795,7 +793,7 @@ { if (autoInterpreterState == NULL) return NULL; - return (PyThreadState *)PyThread_get_key_value(autoTLSkey); + return (PyThreadState *)PyThread_tss_get(autoTLSkey); } int @@ -806,7 +804,7 @@ if (!_PyGILState_check_enabled) return 1; - if (autoTLSkey == -1) + if (!PyThread_tss_is_created(autoTLSkey)) 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(autoTLSkey); 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(autoTLSkey); if (tcur == NULL) Py_FatalError("auto-releasing thread-state, " "but no thread-state for this thread"); diff -r 1c247c8a9802 Python/thread.c --- a/Python/thread.c Mon Jan 09 13:12:09 2017 +0100 +++ b/Python/thread.c Mon Jan 09 21:40:37 2017 +0900 @@ -20,6 +20,7 @@ #include #endif +#include /* necessary for TSS key */ #include #include "pythread.h" @@ -130,20 +131,20 @@ /* ------------------------------------------------------------------------ Per-thread data ("key") support. -Use PyThread_create_key() to create a new key. This is typically shared +Use PyThread_tss_create() 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. */ @@ -247,31 +248,90 @@ 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 + + On 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; + /* On 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); +} + + +/* 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 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 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 +339,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 +359,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 +371,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 +379,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,6 +391,7 @@ 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 diff -r 1c247c8a9802 Python/thread_foobar.h --- a/Python/thread_foobar.h Mon Jan 09 13:12:09 2017 +0100 +++ b/Python/thread_foobar.h Mon Jan 09 21:40:37 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,6 +126,63 @@ } + +/* Thread Specific Storage (TSS) API */ + +int +PyThread_tss_create(Py_tss_t *key) +{ + assert(key != NULL); + /* If 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 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_ReInitTLS(void) { diff -r 1c247c8a9802 Python/thread_nt.h --- a/Python/thread_nt.h Mon Jan 09 13:12:09 2017 +0100 +++ b/Python/thread_nt.h Mon Jan 09 21:40:37 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,71 @@ TlsSetValue(key, NULL); } + +/* Thread Specific Storage (TSS) API */ + +int +PyThread_tss_create(Py_tss_t *key) +{ + assert(key != NULL); + /* If 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; + /* On 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 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 TLS 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 TLS is not necessary after fork when using * the native TLS functions. And forking isn't supported on Windows either. */ diff -r 1c247c8a9802 Python/thread_pthread.h --- a/Python/thread_pthread.h Mon Jan 09 13:12:09 2017 +0100 +++ b/Python/thread_pthread.h Mon Jan 09 21:40:37 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,17 @@ #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. If incompatible with int, PyThread_create_key returns + immediately a failure status, and the other TLS functions all are no-ops. +*/ + 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 +626,99 @@ 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 } + +/* Thread Specific Storage (TSS) API */ + +int +PyThread_tss_create(Py_tss_t *key) +{ + assert(key != NULL); + /* If 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 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. */ + 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_ReInitTLS(void) {} diff -r 1c247c8a9802 configure --- a/configure Mon Jan 09 13:12:09 2017 +0100 +++ b/configure Mon Jan 09 21:40:37 2017 +0900 @@ -8981,6 +8981,74 @@ fi + +# Issue #25658: POSIX hasn't defined that pthread_key_t is compatible with int. +# 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 1c247c8a9802 configure.ac --- a/configure.ac Mon Jan 09 13:12:09 2017 +0100 +++ b/configure.ac Mon Jan 09 21:40:37 2017 +0900 @@ -2258,6 +2258,24 @@ #endif ]) fi + +# Issue #25658: POSIX hasn't defined that pthread_key_t is compatible with int. +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 1c247c8a9802 pyconfig.h.in --- a/pyconfig.h.in Mon Jan 09 13:12:09 2017 +0100 +++ b/pyconfig.h.in Mon Jan 09 21:40:37 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