Issue 19787: Fix PyThread_set_key_value() strange behaviour (original) (raw)

Created on 2013-11-26 00:13 by vstinner, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
pythread_set_key_value.patch vstinner,2013-11-28 01:24 review
fix_set_key_value.patch vstinner,2013-11-28 21:37 review
pythread_set_key_value-2.patch vstinner,2013-12-13 03:17 review
Messages (25)
msg204442 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-11-26 00:13
The tracemalloc module uses PyThread_set_key_value() to store an flag in the Thread Local Storage. The problem is that it is not possible to call the function twice with two different values. If PyThread_set_key_value() is called with a non-NULL pointer, the next calls do nothing. Python should expose a new function which would always call TlsSetValue() / pthread_setspecific() with the input value with no extra check on the input value.
msg204443 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-11-26 00:14
Extract of Modules/_tracemalloc.c: static void set_reentrant(int reentrant) { assert(reentrant == 0 | reentrant == 1); if (reentrant) { assert(PyThread_get_key_value(tracemalloc_reentrant_key) == NULL); PyThread_set_key_value(tracemalloc_reentrant_key, REENTRANT); } else { /* FIXME: PyThread_set_key_value() cannot be used to set the flag to zero, because it does nothing if the variable has already a value set. */ PyThread_delete_key_value(tracemalloc_reentrant_key); } }
msg204634 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-11-28 01:24
Oh, here is in interesting commit: --- changeset: 33705:891042c94aed branch: legacy-trunk user: Tim Peters <tim.peters@gmail.com> date: Sat Oct 09 22:33:09 2004 +0000 files: Python/thread.c description: Document the results of painful reverse-engineering of the "portable TLS" code. PyThread_set_key_value(): It's clear that this code assumes the passed-in value isn't NULL, so document that it must not be, and assert that it isn't. It remains unclear whether existing callers want the odd semantics actually implemented by this function. --- Here is a patch adding a _PyThread_set_key_value() function which behaves as I expected. It looks like PyThread_set_key_value() got its strange behaviour from the find_key() of Python/thread.c. Don't hesistate if you have a better suggestion for the name if the new function :-) Another option is to modify directly the existing function, but it might break applications relying on the current (weird) behaviour.
msg204636 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-11-28 01:29
I ran test_tracemalloc on Linux and Windows and the test passed successfully. It should probably be better to split the patch in two parts if the idea of changing Python/thread* files is accepted. (But initially, the issue comes from the tracemalloc module.) set_reentrant() of tracemalloc.c: + assert(PyThread_get_key_value(tracemalloc_reentrant_key) == REENTRANT); I also added a new assertion to ensure that set_reentrant(0) was not called twice. It was a suggesed of Jim Jewett. This is unrelated to this issue and should be commited separatly.
msg204660 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-11-28 14:35
Calling it _PyThread_set_key_value is prone to confusion. Ideally we would fix PyThread_set_key_value's behaviour. Are there users of the function outside from CPython?
msg204674 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-11-28 16:17
2013/11/28 Antoine Pitrou <report@bugs.python.org>: > Calling it _PyThread_set_key_value is prone to confusion. > Ideally we would fix PyThread_set_key_value's behaviour. Are there users of the function outside from CPython? PyThread_set_key_value() is defined and used in CPython, and defined in the cpyext module of PyPy. I found two usages of the function: "pygobject2:/pygobject-2.28.6/glib/pygmainloop.c" and"pytools:/Servicing/1.1/Release/Product/Python/PyDebugAttach/PyDebugAttach.cpp" http://searchcode.com/codesearch/view/21308375 http://searchcode.com/codesearch/view/10353970 PyThread_delete_key_value() is always called before PyThread_set_key_value() (with the same key). So these projects would not be impacted by the change. I guess that the key is deleted to workaround the current limitation ("strange behaviour") of PyThread_set_key_value(). I cannot find pygmainloop.c in the latest development version: https://git.gnome.org/browse/pygobject/tree/gi/_glib I searched for usage at: http://code.ohloh.net/ https://github.com/search/ http://searchcode.com/ http://stackoverflow.com/search It's hard to find usage of PyThread_set_key_value() before they are a lot of copies of CPython in the search engines, and I don't know how to filter. Victor
msg204676 - (view) Author: Charles-François Natali (neologix) * (Python committer) Date: 2013-11-28 16:51
> Antoine Pitrou added the comment: > > Calling it _PyThread_set_key_value is prone to confusion. > Ideally we would fix PyThread_set_key_value's behaviour. Are there users of the function outside from CPython? The question is why does it behave this way in the first place? Maybe for sub-interpreters?
msg204680 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-11-28 17:20
> The question is why does it behave this way in the first place? > Maybe for sub-interpreters? The code is old. I don't think that it's related to subinterpreter. PyThread_set_key_value() is used in _PyGILState_Init() and PyGILState_Ensure(), but its behaviour when the key already exists doesn't matter because these functions only call PyThread_set_key_value() once pr thread. It was probably simpler to implement PyThread_set_key_value() like it is nowadays. When the native TLS support was added, the new functions just mimic the old behaviour. Code history. find_key() function has been added at the same time than the PyThreadState structure. set_key_value() was already doing nothing when the key already exists. It looks like set_key_value() was not used. --- changeset: 5405:b7871ca930ad branch: legacy-trunk user: Guido van Rossum <guido@python.org> date: Mon May 05 20:56:21 1997 +0000 files: Include/Python.h Include/frameobject.h Include/pystate.h Include/pythread.h Include/thread.h Modules/threadmodule.c Objects/frameobject description: Massive changes for separate thread state management. All per-thread globals are moved into a struct which is manipulated separately. --- TLS only started to be used in CPython since the following major change: --- changeset: 28694:a4154dd5939a branch: legacy-trunk user: Mark Hammond <mhammond@skippinet.com.au> date: Sat Apr 19 15:41:53 2003 +0000 files: Include/pystate.h Include/pythread.h Lib/test/test_capi.py Modules/_testcapimodule.c Modules/posixmodule.c Python/ceval.c Python/pystat description: New PyGILState_ API - implements pep 311, from patch 684256. --- Native TLS support is very recent (compared to the first commit): --- changeset: 64844:8e428b8e7d81 user: Kristján Valur Jónsson <kristjan@ccpgames.com> date: Mon Sep 20 02:11:49 2010 +0000 files: Python/pystate.c Python/thread_nt.h Python/thread_pthread.h description: issue 9786 Native TLS support for pthreads PyThread_create_key now has a failure mode that the applicatino can detect. ---
msg204691 - (view) Author: Kristján Valur Jónsson (kristjan.jonsson) * (Python committer) Date: 2013-11-28 19:43
Deja vu, this has come up before. I wanted to change this because native TLS implementation become awkward. https://mail.python.org/pipermail/python-dev/2008-August/081847.html
msg204692 - (view) Author: Kristján Valur Jónsson (kristjan.jonsson) * (Python committer) Date: 2013-11-28 19:49
See also issue #10517
msg204703 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-11-28 21:37
Attached patch fix PyThread_set_key_value() instead of adding a new function. I prefer fix_set_key_value.patch instead of pythread_set_key_value.patch because I feel bad of keeping a buggy function and having to decide between a correct and a buggy version of the same function. > Deja vu, this has come up before. I wanted to change this because native TLS implementation become awkward. > https://mail.python.org/pipermail/python-dev/2008-August/081847.html You didn't get answer to this old email :-( I suppose that you still want to fix PyThread_set_key_value()?
msg204704 - (view) Author: Kristján Valur Jónsson (kristjan.jonsson) * (Python committer) Date: 2013-11-28 21:55
Please see the rather long discussion in http://bugs.python.org/issue10517 There were issues having to do with fork.
msg204705 - (view) Author: Kristján Valur Jónsson (kristjan.jonsson) * (Python committer) Date: 2013-11-28 21:55
But yes, I'd like to see this behave like normal.
msg204706 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-11-28 22:01
> Please see the rather long discussion in http://bugs.python.org/issue10517 > There were issues having to do with fork. Python has now a _PyGILState_Reinit() function. I don't see how fix_set_key_value.patch would make the situation worse. What is the link between this issue and the issue #10517.
msg204707 - (view) Author: Charles-François Natali (neologix) * (Python committer) Date: 2013-11-28 22:17
AFAICT, there's no link (FWIW I wrote the patch for #10517, then the fix for threads created outside of Python calling into a subinterpreter - issue #13156). Actually, this "fix" would have avoided the regression in issue #13156. But since it's tricky, I'd really like if Graham could test it (his module does a lot of nasty things that could not show up in our test suite).
msg204708 - (view) Author: Kristján Valur Jónsson (kristjan.jonsson) * (Python committer) Date: 2013-11-28 22:24
Only that issue #10517 mentions reasons to keep the old behavior, specifically http://bugs.python.org/issue10517#msg134573 I don't know if any of the old arguments are still valid, but I suggested changing this years ago and there was always some objection or other.
msg205158 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-12-03 21:30
Kristján> Only that issue #10517 mentions reasons to keep the old behavior, specifically http://bugs.python.org/issue10517#msg134573 (...) @Kristján: The behaviour of PyThread_set_key() discussed in this issue is unrelated to the pthread bug related to fork() fixed in #10517. When it comes to threads and fork, I trust neologix because he knows them better than me and he wrote "AFAICT, there's no link".
msg205161 - (view) Author: Charles-François Natali (neologix) * (Python committer) Date: 2013-12-03 21:52
> STINNER Victor added the comment: > > Kristján> Only that issue #10517 mentions reasons to keep the old behavior, specifically http://bugs.python.org/issue10517#msg134573 (...) > > @Kristján: The behaviour of PyThread_set_key() discussed in this issue is unrelated to the pthread bug related to fork() fixed in #10517. > > When it comes to threads and fork, I trust neologix because he knows them better than me and he wrote "AFAICT, there's no link". It's unrelated, but I don't know if changing the current semantics could break some code, especially involving subinterpreters: that's why i'd like to have it tested. But the current behavior is *really* a pain: not having PyThread_set_key(key, value) assert(PyThread_get_key(key) == value) sounds really wrong.
msg205216 - (view) Author: Kristján Valur Jónsson (kristjan.jonsson) * (Python committer) Date: 2013-12-04 11:27
+1 Why don't we just fix this and see where the chips fall?
msg205985 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-12-12 21:59
If I understand correctly, there is probably only one application (mod_python) which might be broken by fix_set_key_value.patch. If an application is broken by fix_set_key_value.patch, it can get the old behaviour using: int PyThread_set_key_value33(int key, void *value) { #if PY_VERSION_HEX >= 0x03040000 void *oldValue = PyThread_get_key_value(key); if (oldValue != NULL) return 0; #endif return PyThread_set_key_value(key, value); } So are you ok to apply the bugfix in Python 3.4?
msg205986 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2013-12-12 22:00
On jeu., 2013-12-12 at 21:59 +0000, STINNER Victor wrote: > So are you ok to apply the bugfix in Python 3.4? I'm ok.
msg206014 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2013-12-13 02:32
New changeset 46393019b650 by Victor Stinner in branch 'default': Close #19787: PyThread_set_key_value() now always set the value. In Python 3.3, http://hg.python.org/cpython/rev/46393019b650
msg206015 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-12-13 02:35
Oh, my change on PyThread_set_key_value() has an unexpected effect on _testcapi.run_in_subinterp(): it now fixes the Python thread state. Py_NewInterpreter() creates a second Python thread state for the current thread, but PyThread_set_key_value() ignored the second call setting the new thread state. It's a little bit strange that nobody noticed this bug before. It doesn't fix issue #10915: test_threading still hangs when tracemalloc is enabled (I modified manually test.support.run_in_subinterp() for a manual test). This issue should now be fixed.
msg206023 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2013-12-13 03:17
My commit broke test_capi, so I revert it. Here is the commit as a patch. The impact on subinterpreters is more complex than what I expected. A decision should be take on what to do: mimic behaviour of Python 3.3 for subinterpreters (don't replace the Python thread state when a new subinterpreter is created), or fix code using subinterpreters.
msg206043 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2013-12-13 10:14
New changeset 5d078b0bae75 by Victor Stinner in branch 'default': Issue #19787: PyThread_set_key_value() now always set the value http://hg.python.org/cpython/rev/5d078b0bae75
History
Date User Action Args
2022-04-11 14:57:54 admin set github: 63986
2013-12-13 11:11:50 vstinner set status: open -> closedresolution: fixed
2013-12-13 10:14:21 python-dev set messages: +
2013-12-13 03:17:45 vstinner set status: closed -> openfiles: + pythread_set_key_value-2.patchresolution: fixed -> (no value)messages: +
2013-12-13 02:35:29 vstinner set messages: +
2013-12-13 02:32:23 vstinner set title: tracemalloc: set_reentrant() should not have to call PyThread_delete_key() -> Fix PyThread_set_key_value() strange behaviour
2013-12-13 02:32:05 python-dev set status: open -> closednosy: + python-devmessages: + resolution: fixedstage: resolved
2013-12-12 22:00:04 pitrou set messages: +
2013-12-12 21:59:31 vstinner set versions: + Python 3.4
2013-12-12 21:59:23 vstinner set messages: +
2013-12-04 11:27:38 kristjan.jonsson set messages: +
2013-12-03 21:52:17 neologix set messages: +
2013-12-03 21:30:23 vstinner set messages: +
2013-11-28 22:24:21 kristjan.jonsson set messages: +
2013-11-28 22:17:38 neologix set nosy: + grahamdmessages: +
2013-11-28 22:01:55 vstinner set messages: +
2013-11-28 21:55:56 kristjan.jonsson set messages: +
2013-11-28 21:55:22 kristjan.jonsson set messages: +
2013-11-28 21:37:27 vstinner set files: + fix_set_key_value.patchmessages: +
2013-11-28 19:49:13 kristjan.jonsson set messages: +
2013-11-28 19:43:33 kristjan.jonsson set messages: +
2013-11-28 17:20:45 vstinner set nosy: + kristjan.jonssonmessages: +
2013-11-28 16:51:16 neologix set messages: +
2013-11-28 16:17:52 vstinner set messages: +
2013-11-28 14:35:53 pitrou set messages: +
2013-11-28 01:29:49 vstinner set messages: +
2013-11-28 01:25:04 vstinner set files: + pythread_set_key_value.patchnosy: + tim.peters, pitroumessages: + keywords: + patch
2013-11-26 02:27:57 Arfrever set nosy: + Arfrever
2013-11-26 01:45:33 pitrou set type: enhancementversions: + Python 3.5, - Python 3.4
2013-11-26 00:14:30 vstinner set messages: +
2013-11-26 00:13:57 vstinner create