bpo-39877: Fix PyEval_RestoreThread() for daemon threads#18811
Conversation
|
Without this change, after Py_Finalize() has been called, daemon threads are not exited by exit_thread_if_finalizing(): exit_thread_if_finalizing() is not called. Instead, daemon threads loop on attempting to take the GIL :-( With this PR, daemon threads exit before attempting to take the GIL. Moreover, the PR better validates tstate which can help to detect misusage of modified functions of the C API like PyEval_RestoreThread(). |
Sorry, something went wrong.
|
@ericsnowcurrently @nanjekyejoannah @pablogsal @pitrou @methane @serhiy-storchaka: Would you mind to review this PR? In short, this PR fix the code to exit daemon threads after Py_Finalize() has been called. Currently, it's broken in multiple different ways. But it worked somehow before my latest refactoring (see https://bugs.python.org/issue39877 for the cause of the regression). Main changes:
I chose to include "cleanup" changes in the PR: harden checks on tstate. I prefer to change all code at once. It should help to detect future bugs. |
Sorry, something went wrong.
pitrou
left a comment
There was a problem hiding this comment.
Looks good on the principle, some comments below.
Sorry, something went wrong.
|
When you're done making the requested changes, leave the comment: |
Sorry, something went wrong.
|
I have made the requested changes; please review again. |
Sorry, something went wrong.
|
Thanks for making the requested changes! @pitrou: please review the changes made to this pull request. |
Sorry, something went wrong.
* exit_thread_if_finalizing() does now access directly _PyRuntime variable, rather than using tstate->interp->runtime since tstate can be a dangling pointer after Py_Finalize() has been called. * exit_thread_if_finalizing() is now called *before* calling take_gil(). There is no need to protect the call with the GIL. * Add ensure_tstate_not_null() function to check that tstate is not NULL at runtime. Check tstate earlier. take_gil() is now responsible to check that tstate is not NULL. Cleanup: * PyEval_RestoreThread() no longer saves/restores errno: it's already done inside take_gil(). * PyEval_AcquireLock(), PyEval_AcquireThread(), PyEval_RestoreThread() and _PyEval_EvalFrameDefault() now check if tstate is valid with the new is_tstate_valid() function which uses _PyMem_IsPtrFreed().
|
@pitrou: _PyRuntime.finalizing is now atomic. Would you mind to review the updated PR? |
Sorry, something went wrong.
|
Thanks for the review @python/buildbot-owners Daemon threads should be a little bit more reliable during Python finalization with this change. |
Sorry, something went wrong.
Oops, I wanted to write: Thanks for the review @pitrou! (Firefox picked the wrong completion, sorry.) |
Sorry, something went wrong.
bpo-39877: Fix PyEval_RestoreThread() for daemon threads (pythonGH-18811)
variable, rather than using tstate->interp->runtime since tstate
can be a dangling pointer after Py_Finalize() has been called.
take_gil(). There is no need to protect the call with the GIL.
at runtime. Check tstate earlier. take_gil() is now responsible to
check that tstate is not NULL.
Cleanup:
done inside take_gil().
PyEval_RestoreThread() and _PyEval_EvalFrameDefault() now check if
tstate is valid with the new is_tstate_valid() function which uses
_PyMem_IsPtrFreed().
https://bugs.python.org/issue39877