-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
fix: make gil_safe_call_once thread-safe in free-threaded CPython #5246
Conversation
The "is_initialized_" flags is not protected by the GIL in free-threaded Python, so it needs to be an atomic field. Fixes pybind#5245
@@ -7,6 +7,9 @@ | |||
|
|||
#include <cassert> | |||
#include <mutex> | |||
#ifdef Py_GIL_DISABLED |
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.
Slight preference for an empty line before the #ifdef
.
Hi @colesbury, considering that Python 3.13.0rc1 is out, we (me, @henryiii, @EthanSteinberg ) are thinking it may be useful to make another pybind11 release, with this PR included. We are wondering: Is there anything else obvious (from your vantage point) that we could/should do in pybind11, before making a release? |
No, I can't think of anything else |
@colesbury I'm a bit worried about the logic in https://github.com/pybind/pybind11/blob/master/include/pybind11/detail/internals.h#L498 under free-threading and I was hoping I could get your help to double check that everything is thread safe. internals is a global singleton struct shared across all pybind11 modules. get_internals() either retrieves that current global singleton or creates a new one. I'm a bit worried about double initialization, where two copies of internals would be created by multiple modules being imported/initalized in different threads. https://github.com/pybind/pybind11/blob/master/include/pybind11/detail/internals.h#L520-L524 are the key problematic lines where we retrieve the current global, and initialize it if necessary. The problem is that if two threads hit those lines at the same time, both threads could see that internals is null and both threads could initialize internals, which would lead to two copies. Currently this is protected via the GIL (https://github.com/pybind/pybind11/blob/master/include/pybind11/detail/internals.h#L505), but does that work with the new free threading code? |
I think it would be better if we made the initialization in Do you want to create an issue for this? |
Another question (based on a question from @EthanSteinberg in separate communication): would it make sense for |
No, I think |
@colesbury Thanks for confirming that the GIL is enabled during module initialization, so this is probably not a concern. I don't know if it makes sense to make an issue now for this. Instead, let me write a PR next weekend that just adds some comments to the code pointing out why the module initialization GIL behavior allows that code to be safe even with free threading. |
I'd rather not rely on the GIL behavior during module initialization. Additionally, at least in theory, I think |
I'm not sure if it's helpful, but JIC,
|
) * fix: Make gil_safe_call_once thread-safe in free-threaded CPython The "is_initialized_" flags is not protected by the GIL in free-threaded Python, so it needs to be an atomic field. Fixes #5245 * style: pre-commit fixes * Apply changes from code review --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
@colesbury @rwgk I created an issue to track / solve this #5316 |
Description
The "is_initialized_" flags is not protected by the GIL in free-threaded Python, so it needs to be an atomic field.
Fixes #5245
Suggested changelog entry:
Make ``gil_safe_call_once_and_store`` thread-safe in free-threaded CPython.