Commit 590cebe3 authored by Victor Stinner's avatar Victor Stinner

Issue #19787: PyThread_set_key_value() now always set the value

In Python 3.3, PyThread_set_key_value() did nothing if the key already exists
(if the current value is a non-NULL pointer).

When _PyGILState_NoteThreadState() is called twice on the same thread with a
different Python thread state, it still keeps the old Python thread state to
keep the old behaviour. Replacing the Python thread state with the new state
introduces new bugs: see issues #10915 and #15751.
parent cb1c4c8c
...@@ -10,6 +10,10 @@ Release date: 2014-01-05 ...@@ -10,6 +10,10 @@ Release date: 2014-01-05
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #19787: PyThread_set_key_value() now always set the value. In Python
3.3, the function did nothing if the key already exists (if the current value
is a non-NULL pointer).
- Issue #14432: Remove the thread state field from the frame structure. Fix a - Issue #14432: Remove the thread state field from the frame structure. Fix a
crash when a generator is created in a C thread that is destroyed while the crash when a generator is created in a C thread that is destroyed while the
generator is still used. The issue was that a generator contains a frame, and generator is still used. The issue was that a generator contains a frame, and
......
...@@ -168,14 +168,11 @@ set_reentrant(int reentrant) ...@@ -168,14 +168,11 @@ set_reentrant(int reentrant)
assert(reentrant == 0 || reentrant == 1); assert(reentrant == 0 || reentrant == 1);
if (reentrant) { if (reentrant) {
assert(PyThread_get_key_value(tracemalloc_reentrant_key) == NULL); assert(PyThread_get_key_value(tracemalloc_reentrant_key) == NULL);
PyThread_set_key_value(tracemalloc_reentrant_key, PyThread_set_key_value(tracemalloc_reentrant_key, REENTRANT);
REENTRANT);
} }
else { else {
/* FIXME: PyThread_set_key_value() cannot be used to set the flag assert(PyThread_get_key_value(tracemalloc_reentrant_key) == REENTRANT);
to zero, because it does nothing if the variable has already PyThread_set_key_value(tracemalloc_reentrant_key, NULL);
a value set. */
PyThread_delete_key_value(tracemalloc_reentrant_key);
} }
} }
......
...@@ -723,18 +723,18 @@ _PyGILState_NoteThreadState(PyThreadState* tstate) ...@@ -723,18 +723,18 @@ _PyGILState_NoteThreadState(PyThreadState* tstate)
The only situation where you can legitimately have more than one The only situation where you can legitimately have more than one
thread state for an OS level thread is when there are multiple thread state for an OS level thread is when there are multiple
interpreters, when: interpreters.
a) You shouldn't really be using the PyGILState_ APIs anyway, You shouldn't really be using the PyGILState_ APIs anyway (see issues
and: #10915 and #15751).
b) The slightly odd way PyThread_set_key_value works (see The first thread state created for that given OS level thread will
comments by its implementation) means that the first thread "win", which seems reasonable behaviour.
state created for that given OS level thread will "win",
which seems reasonable behaviour.
*/ */
if (PyThread_set_key_value(autoTLSkey, (void *)tstate) < 0) if (PyThread_get_key_value(autoTLSkey) == NULL) {
Py_FatalError("Couldn't create autoTLSkey mapping"); if (PyThread_set_key_value(autoTLSkey, (void *)tstate) < 0)
Py_FatalError("Couldn't create autoTLSkey mapping");
}
/* PyGILState_Release must not try to delete this thread state. */ /* PyGILState_Release must not try to delete this thread state. */
tstate->gilstate_counter = 1; tstate->gilstate_counter = 1;
......
...@@ -205,7 +205,7 @@ static int nkeys = 0; /* PyThread_create_key() hands out nkeys+1 next */ ...@@ -205,7 +205,7 @@ static int nkeys = 0; /* PyThread_create_key() hands out nkeys+1 next */
* segfaults. Now we lock the whole routine. * segfaults. Now we lock the whole routine.
*/ */
static struct key * static struct key *
find_key(int key, void *value) find_key(int set_value, int key, void *value)
{ {
struct key *p, *prev_p; struct key *p, *prev_p;
long id = PyThread_get_thread_ident(); long id = PyThread_get_thread_ident();
...@@ -215,8 +215,11 @@ find_key(int key, void *value) ...@@ -215,8 +215,11 @@ find_key(int key, void *value)
PyThread_acquire_lock(keymutex, 1); PyThread_acquire_lock(keymutex, 1);
prev_p = NULL; prev_p = NULL;
for (p = keyhead; p != NULL; p = p->next) { for (p = keyhead; p != NULL; p = p->next) {
if (p->id == id && p->key == key) if (p->id == id && p->key == key) {
if (set_value)
p->value = value;
goto Done; goto Done;
}
/* Sanity check. These states should never happen but if /* Sanity check. These states should never happen but if
* they do we must abort. Otherwise we'll end up spinning in * they do we must abort. Otherwise we'll end up spinning in
* in a tight loop with the lock held. A similar check is done * in a tight loop with the lock held. A similar check is done
...@@ -227,7 +230,7 @@ find_key(int key, void *value) ...@@ -227,7 +230,7 @@ find_key(int key, void *value)
if (p->next == keyhead) if (p->next == keyhead)
Py_FatalError("tls find_key: circular list(!)"); Py_FatalError("tls find_key: circular list(!)");
} }
if (value == NULL) { if (!set_value && value == NULL) {
assert(p == NULL); assert(p == NULL);
goto Done; goto Done;
} }
...@@ -279,19 +282,12 @@ PyThread_delete_key(int key) ...@@ -279,19 +282,12 @@ PyThread_delete_key(int key)
PyThread_release_lock(keymutex); PyThread_release_lock(keymutex);
} }
/* Confusing: If the current thread has an association for key,
* value is ignored, and 0 is returned. Else an attempt is made to create
* an association of key to value for the current thread. 0 is returned
* if that succeeds, but -1 is returned if there's not enough memory
* to create the association. value must not be NULL.
*/
int int
PyThread_set_key_value(int key, void *value) PyThread_set_key_value(int key, void *value)
{ {
struct key *p; struct key *p;
assert(value != NULL); p = find_key(1, key, value);
p = find_key(key, value);
if (p == NULL) if (p == NULL)
return -1; return -1;
else else
...@@ -304,7 +300,7 @@ PyThread_set_key_value(int key, void *value) ...@@ -304,7 +300,7 @@ PyThread_set_key_value(int key, void *value)
void * void *
PyThread_get_key_value(int key) PyThread_get_key_value(int key)
{ {
struct key *p = find_key(key, NULL); struct key *p = find_key(0, key, NULL);
if (p == NULL) if (p == NULL)
return NULL; return NULL;
......
...@@ -389,20 +389,11 @@ PyThread_delete_key(int key) ...@@ -389,20 +389,11 @@ PyThread_delete_key(int key)
TlsFree(key); TlsFree(key);
} }
/* We must be careful to emulate the strange semantics implemented in thread.c,
* where the value is only set if it hasn't been set before.
*/
int int
PyThread_set_key_value(int key, void *value) PyThread_set_key_value(int key, void *value)
{ {
BOOL ok; BOOL ok;
void *oldvalue;
assert(value != NULL);
oldvalue = TlsGetValue(key);
if (oldvalue != NULL)
/* ignore value if already set */
return 0;
ok = TlsSetValue(key, value); ok = TlsSetValue(key, value);
if (!ok) if (!ok)
return -1; return -1;
......
...@@ -627,9 +627,6 @@ int ...@@ -627,9 +627,6 @@ int
PyThread_set_key_value(int key, void *value) PyThread_set_key_value(int key, void *value)
{ {
int fail; int fail;
void *oldValue = pthread_getspecific(key);
if (oldValue != NULL)
return 0;
fail = pthread_setspecific(key, value); fail = pthread_setspecific(key, value);
return fail ? -1 : 0; return fail ? -1 : 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment