-
-
Notifications
You must be signed in to change notification settings - Fork 31.7k
gh-126907: Lock the atexit
state on the free-threaded build
#126908
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
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.
Quick review while waiting for my train
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.
Overall, IMO the approach is valid. Here is a first review.
Co-authored-by: Victor Stinner <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>
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.
@colesbury @mpage: Do you want want to review this change?
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 think it's easy to introduce reentrancy issues in this code, how about using a critical section instead of mutex?
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.
Thanks for taking this on Peter. Like @kumaraditya303 suggested, I think using critical sections will simplify the code and avoid some re-entrancy issues. Having to explicitly unlock the mutex inside some functions is a sign that the code should be refactored:
The low-level callbacks (ll_callbacks
) and high-level Python callbacks are pretty different:
- The
PyMutex
for modifyingll_callbacks
is fine - Use a critical section for protecting
state->callbacks
. Ideally, the acquisition happens at the module boundaries. Use argument clinic and@critical_section
for the module methods. - I suspect a lot of the code would be simplified if
state->callbacks
were a Python list object
_PyAtExit_LOCK(state); | ||
if (state->callbacks[i] == NULL) |
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 explicit unlocking means that this still has re-entrancy and concurrency issues if the callback list is modified during the comparison. callbacks[i]
may be a different callback.
Using critical sections doesn't completely avoid the problem if __eq__
is particularly weird, but it avoids it for the common case of identity comparison and the issues are limited to the cases where the GIL-enabled build has issues as well.
Ok, I'll switch this over to critical sections. For now, I'd rather just ignore
It could, but it also might be a PITA because we'd need to introduce capsules into the mix. I'll play around with it and see how it looks. |
I don't think you need capsules, which I agree would be a pain. I think you can use a tuple of three items instead of Most of what The Python callbacks are executed while other threads are still running (and can possibly modify the callbacks). Using a PyListObject makes it easier to make a local copy of the callbacks before iterating over them, which avoids some re-entrancy and thread-safety issues in |
To be clear, I am only suggesting a PyListObject instead of |
I was just thinking about using a list because it has a nicer interface, not for thread safety.
They are? That's not good. The two places that execute atexit callbacks (apart from manually doing it) are Line 2040 in 5121685
Line 2440 in 5121685
Both of which are supposed to happen when no threads are left. I guess daemon and C threads could screw things up, but I'm willing to write that off as a "play stupid games, win stupid prizes." |
Marking as |
You might create a new PR if you switch to critical sections, so we can compare the two approaches, and the PR history is easier to follow. |
Closing now that #127935 got merged. Thanks to everyone that reviewed! |
atexit
from threads in free-threading build segfaults #126907