You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PyGILState_Ensure on non-Python thread causes fatal error #65090
In Python 3.4rc3, calling PyGILState_Ensure() from a thread that was not created by Python and without any calls to PyEval_InitThreads() will cause a fatal exit:
Fatal Python error: take_gil: NULL tstate
I believe this was added in http://hg.python.org/cpython/rev/dc4e805ec68a. The attached file has a minimal repro (for Windows, sorry, but someone familiar with low-level *nix threading APIs should be able to translate it easily).
Basically, the sequence looks like this:
main() calls Py_Initialize()
main() starts new C-level thread
main() calls Py_BEGIN_ALLOW_THREADS and waits for the new thread
New thread calls PyGILState_Ensure()
PyGILState_Ensure() sees that there is no thread state and calls PyEval_InitThreads() (this is what changed in the linked changeset)
PyEval_InitThreads() creates the GIL
PyEval_InitThreads() calls take_gil(PyThreadState_GET()) (this is what aborts - there is still no thread state)
(expected) PyGILState_Ensure() calls PyThreadState_New() and PyEval_RestoreThread().
The attached repro has two workarounds (commented out). The first is to call PyThreadState_New()/PyEval_RestoreThread() from the new thread, and the second is to call PyEval_InitThreads() from the main thread.
The latter is inappropriate for my use, since I am writing a debugger that can inject itself into a running CPython process - there is no way to require that PyEval_InitThreads() has been called and I can't reliably inject it onto the main thread. The first workaround is okay, but I'd rather not have to special-case 3.4 by rewriting PyGILState_Ensure() so that I can safely call PyGILState_Ensure().
Ok, the bug is now fixed in Python 2.7, 3.6 and master (future 3.7). On 3.6 and master, the fix comes with an unit test.
Thanks Steve Dower for the bug report, sorry for the long delay, I completely forgot this old bug!
Marcin Kasperski: Thanks for the reminder. Sadly, we will have to wait for the next release to get the fix. In the meanwhile, you can workaround the bug by calling PyEval_InitThreads() after Py_Initialize() but before running actual code.
For 2.7 I hesitate to OK this, who knows what skeletons are still in that
closet. It doesn't sound like a security fix to me. For 3.6 I'm fine with
it as a bugfix.
Victor, could you run your patch through the performance benchmarks? While I suspect Antoine is right that our current GIL management heuristics will mean we don't need the lazy initialisation optimisation any more, it's still preferable to have the specific numbers before merging it.
I proposed PR 5421 to fix Python 3.6: modify Py_Initialize() to call PyEval_InitThreads().
I'm not sure of Python 2.7. Not only the backport is not straighforward, but I'm not sure that I want to touch the super-stable 2.7 branch. Maybe for 2.7, I should just remove the currently skipped test_bpo20891() test from test_capi.
Antoine Pitrou considers that my PR 5421 for Python 3.6 should not be merged:
"@vstinner I don't think so. People can already call PyEval_InitThreads." #5421 (comment)
@vstinner I don't think so. People can already call PyEval_InitThreads.
Since only two users complained about https://bugs.python.org/issue20891 in 3 years, I agree that it's ok to not fix Python 2.7 and 3.6. The workaround is to call PyEval_InitThreads() before starting the first thread.
I wasn't excited to make such stable in stable 2.7 and 3.6 anyway :-)
test_embed.test_bpo20891() is enabled again on master. I now close the issue.
Activity
zooba commentedon Mar 11, 2014
In Python 3.4rc3, calling PyGILState_Ensure() from a thread that was not created by Python and without any calls to PyEval_InitThreads() will cause a fatal exit:
Fatal Python error: take_gil: NULL tstate
I believe this was added in http://hg.python.org/cpython/rev/dc4e805ec68a. The attached file has a minimal repro (for Windows, sorry, but someone familiar with low-level *nix threading APIs should be able to translate it easily).
Basically, the sequence looks like this:
The attached repro has two workarounds (commented out). The first is to call PyThreadState_New()/PyEval_RestoreThread() from the new thread, and the second is to call PyEval_InitThreads() from the main thread.
The latter is inappropriate for my use, since I am writing a debugger that can inject itself into a running CPython process - there is no way to require that PyEval_InitThreads() has been called and I can't reliably inject it onto the main thread. The first workaround is okay, but I'd rather not have to special-case 3.4 by rewriting PyGILState_Ensure() so that I can safely call PyGILState_Ensure().
zooba commentedon Mar 11, 2014
Should have linked to bpo-19576 as well, which is the issue associated with that changeset.
vstinner commentedon Mar 13, 2014
IMO it's a bug in PyEval_InitThreads().
vstinner commentedon Mar 15, 2016
ptest.c: Portable example using Python API.
vstinner commentedon Mar 15, 2016
Attached patch should fix the issue.
Mekk commentedon Nov 27, 2017
Is this fix released? I can't find it in the changelog…
(I faced this bug on 3.5.2, released a couple of months after this bug was closed…)
vstinner commentedon Nov 30, 2017
Oops, I lost track of this issue, but it wasn't fixed, no.
I just proposed my old fix as a pull requets: PR 4650.
The workaround is to call PyEval_InitThreads() before spawning your first non-Python thread.
https://docs.python.org/dev/c-api/init.html#c.PyEval_InitThreads
vstinner commentedon Nov 30, 2017
I have to check if Python 2.7 is impacted as well.
vstinner commentedon Nov 30, 2017
New changeset b4d1e1f by Victor Stinner in branch 'master':
bpo-20891: Fix PyGILState_Ensure() (bpo-4650)
b4d1e1f
vstinner commentedon Nov 30, 2017
New changeset be6b74c by Victor Stinner in branch '2.7':
bpo-20891: Fix PyGILState_Ensure() (bpo-4650) (bpo-4657)
be6b74c
vstinner commentedon Nov 30, 2017
New changeset e10c9de by Victor Stinner in branch '3.6':
bpo-20891: Fix PyGILState_Ensure() (bpo-4650) (bpo-4655)
e10c9de
vstinner commentedon Nov 30, 2017
Ok, the bug is now fixed in Python 2.7, 3.6 and master (future 3.7). On 3.6 and master, the fix comes with an unit test.
Thanks Steve Dower for the bug report, sorry for the long delay, I completely forgot this old bug!
Marcin Kasperski: Thanks for the reminder. Sadly, we will have to wait for the next release to get the fix. In the meanwhile, you can workaround the bug by calling PyEval_InitThreads() after Py_Initialize() but before running actual code.
7 remaining items
vstinner commentedon Dec 4, 2017
I wrote the PR 4700 to create the GIL in Py_Initialize().
Would you be ok to backport this fix to Python 2.7 and 3.6 as well?
gvanrossum commentedon Dec 4, 2017
For 2.7 I hesitate to OK this, who knows what skeletons are still in that
closet. It doesn't sound like a security fix to me. For 3.6 I'm fine with
it as a bugfix.
ncoghlan commentedon Dec 5, 2017
+1 for making this change 3.6+ only.
Victor, could you run your patch through the performance benchmarks? While I suspect Antoine is right that our current GIL management heuristics will mean we don't need the lazy initialisation optimisation any more, it's still preferable to have the specific numbers before merging it.
vstinner commentedon Dec 18, 2017
I ran pyperformance on my PR 4700. Differences of at least 5%:
haypo@speed-python$ python3 -m perf compare_to ~/json/uploaded/2017-12-18_12-29-master-bd6ec4d79e85.json.gz /home/haypo/json/patch/2017-12-18_12-29-master-bd6ec4d79e85-patch-4700.json.gz --table --min-speed=5
+----------------------+--------------------------------------+-------------------------------------------------+
| Benchmark | 2017-12-18_12-29-master-bd6ec4d79e85 | 2017-12-18_12-29-master-bd6ec4d79e85-patch-4700 |
+======================+======================================+=================================================+
| pathlib | 41.8 ms | 44.3 ms: 1.06x slower (+6%) |
+----------------------+--------------------------------------+-------------------------------------------------+
| scimark_monte_carlo | 197 ms | 210 ms: 1.07x slower (+7%) |
+----------------------+--------------------------------------+-------------------------------------------------+
| spectral_norm | 243 ms | 269 ms: 1.11x slower (+11%) |
+----------------------+--------------------------------------+-------------------------------------------------+
| sqlite_synth | 7.30 us | 8.13 us: 1.11x slower (+11%) |
+----------------------+--------------------------------------+-------------------------------------------------+
| unpickle_pure_python | 707 us | 796 us: 1.13x slower (+13%) |
+----------------------+--------------------------------------+-------------------------------------------------+
Not significant (55): 2to3; chameleon; chaos; (...)
vstinner commentedon Dec 21, 2017
New changeset 550ee05 by Victor Stinner in branch 'master':
bpo-20891: Skip test_embed.test_bpo20891() (bpo-4967)
550ee05
vstinner commentedon Dec 21, 2017
New changeset 2e1ef00 by Victor Stinner in branch '3.6':
bpo-20891: Skip test_embed.test_bpo20891() (bpo-4967) (bpo-4969)
2e1ef00
vstinner commentedon Jan 29, 2018
I tested again these 5 benchmarks were Python was slower with my PR. I ran these benchmarks manually on my laptop using CPU isolation. Result:
vstinner@apu$ python3 -m perf compare_to ref.json patch.json --table
Not significant (5): unpickle_pure_python; sqlite_synth; spectral_norm; pathlib; scimark_monte_carlo
Ok, that was expected: no significant difference.
vstinner commentedon Jan 29, 2018
New changeset 2914bb3 by Victor Stinner in branch 'master':
bpo-20891: Py_Initialize() now creates the GIL (bpo-4700)
2914bb3
vstinner commentedon Jan 29, 2018
I proposed PR 5421 to fix Python 3.6: modify Py_Initialize() to call PyEval_InitThreads().
I'm not sure of Python 2.7. Not only the backport is not straighforward, but I'm not sure that I want to touch the super-stable 2.7 branch. Maybe for 2.7, I should just remove the currently skipped test_bpo20891() test from test_capi.
What do you think?
vstinner commentedon Jan 29, 2018
Antoine Pitrou considers that my PR 5421 for Python 3.6 should not be merged:
"@vstinner I don't think so. People can already call PyEval_InitThreads."
#5421 (comment)
vstinner commentedon Jan 29, 2018
New changeset d951157 by Victor Stinner in branch 'master':
bpo-20891: Reenable test_embed.test_bpo20891() (GH-5420)
d951157
vstinner commentedon Jan 29, 2018
Antoine Pitrou:
Since only two users complained about https://bugs.python.org/issue20891 in 3 years, I agree that it's ok to not fix Python 2.7 and 3.6. The workaround is to call PyEval_InitThreads() before starting the first thread.
I wasn't excited to make such stable in stable 2.7 and 3.6 anyway :-)
test_embed.test_bpo20891() is enabled again on master. I now close the issue.
Thanks Steve Dower for the bug report!
vstinner commentedon Jan 29, 2018
New changeset 0cecc22 by Victor Stinner in branch '3.6':
bpo-20891: Remove test_capi.test_bpo20891() (bpo-5425)
0cecc22