-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
bpo-36475: Finalize PyEval_AcquireLock() and PyEval_AcquireThread() properly #12667
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
cc @ericsnowcurrently , @ncoghlan . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but I'd like Eric to take a look as well. In particular, I'm not sure if we have any existing test cases for this behaviour, and whether it might make sense to add one to the embedding tests.
Misc/NEWS.d/next/Core and Builtins/2019-04-02-20-02-22.bpo-36475.CjRps3.rst
Outdated
Show resolved
Hide resolved
@pablogsal @pitrou: Would you mind to review this change? It seems like you know the modified functions :-) |
I would recommend adding a note in the doc for these functions as we did in fde9b33 . Also, this code appears 3 times now in |
Misc/NEWS.d/next/Core and Builtins/2019-04-02-20-02-22.bpo-36475.CjRps3.rst
Outdated
Show resolved
Hide resolved
Hmm... this will exit any thread during finalization, not just daemon threads? I'm not sure why this is a good idea. |
I have made the requested changes; please review again. |
Thanks for making the requested changes! @ncoghlan: please review the changes made to this pull request. |
@pitrou Line 1150 in 0ef8c15
This PR just makes the lower level GIL acquisition APIs behave the same way PyEval_RestoreThread already does so that surviving daemon threads will exit rather than risking deadlocking based on exactly which API they call. This does highlight that the change is worth mentioning in the porting section of the Python 3.8 What's New, though, along with "versionchanged" notes on the affected APIs. |
I see. I'm always wary of the potential issues with such low-level changes, but you're right that we're already half way to the behaviour established by the PR, so I guess it's fine. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's ok on the principle. Please see comments below.
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The core functionality of the change still looks good to me, just some refactoring suggestions for the code, and a couple more documentation tweaks.
My proposed wording for the NEWS entry can be re-used as a bullet point in the What's New porting section, with an extra sentence on what to do about it:
PyEval_AcquireLock
andPyEval_AcquireThread
now terminate the current thread if called while the interpreter is finalizing, making them consistent withPyEval_RestoreThread
,Py_END_ALLOW_THREADS
, andPyGILState_Ensure
. If this behaviour is not desired, guard the call by checking_Py_IsFinalizing
orsys.is_finalizing
.
Misc/NEWS.d/next/Core and Builtins/2019-04-02-20-02-22.bpo-36475.CjRps3.rst
Outdated
Show resolved
Hide resolved
Doc/c-api/init.rst
Outdated
.. note:: | ||
Calling this function from a thread when the runtime is finalizing | ||
will terminate the thread, even if the thread was not created by Python. | ||
You can use :c:func:`_Py_IsFinalizing` or :func:`sys.is_finalizing` to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vstinner Are you aware of any particular reason for _Py_IsFinalizing
being a nominally private API? If we're recommending that folks call it in the documentation, that's a pretty strong sign that it should really be public.
(That can be broken out to a separate issue, though - it doesn't need to be resolved here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. Feel free to open an issue to propose to make it public if you want.
In my PR #12934, I modified the internal _Py_CURRENTLY_FINALIZING() macro to add a runtime parameter :-) (PR currently under review.)
fe8a3aa
to
4f6b32d
Compare
I have made the requested changes; please review again |
FYI I wrote PR #12453 "Destroy the GIL at exit". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me now (noting the follow-up PR in #12679 to switch the thread cleanup to be per-interpreter rather than per-runtime)
Doc/c-api/init.rst
Outdated
:c:func:`PyEval_RestoreThread` is a higher-level function which is always | ||
available (even when threads have not been initialized). | ||
|
||
.. note:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ended up with two copies of this note.
I have made the requested changes; please review again |
I am very scared by this change touching critical parts of Python, but I'm somehow confident that it's the right thing to do. Antoine: Py_Finalize() explicitly waits until all non-daemon threads complete, so this change should impact "regular" threads: only daemon threads. Note: I have no idea of the impact on performances, but IMHO correctness matters more than performance here. If there is an overhead, I expect it to be not significant on macro benchmarks, and low on micro-benchmarks. -- Side note. I really hate the whole concept of daemon threads. Kill a thread anytime is just a very high risk of inconsistency. The thread can be killed while writing in the middle of a file, it can leave temporary files, etc. I would prefer to remove the concept of daemon threads and require developers to call pthread_kill() to make them understand what they are doing (something stupid IMHO :-)). Daemon threads make Python finalization very fragile, but the GIL + this PR make the finalization less fragile. Hint: search for "daemon" in Google Image. IMHO the name is well chosen. |
I am not comfortable to backport this change to Python 3.7. It's too early to know how it will impact applications and how many complains we will get :-) If someone really wants to backport this scary change to 3.7, I would suggest to wait for 1 month after Python 3.8.0 final release. |
I have added changes to finalize finalize
PyEval_AcquireLock()
andPyEval_AcquireThread()
properly.https://bugs.python.org/issue36475