-
Notifications
You must be signed in to change notification settings - Fork 16k
Add support for free-threaded CPython build #23202
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
base: main
Are you sure you want to change the base?
Conversation
* Add recursive mutex locking for ObjCache * Allocate c_descriptor_symtab on init. * Use critical section for DescriptorPool
Drop this patch before merging PR, fix this some other way.
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
This is a more useful condition to check.
Dealloc methods call _Delete, not _DeleteLockHeld so we need to handle the missing module state here to avoid crashing on shutdown.
|
Just a heads up, I've been testing |
In free-threaded build, using weak references adds some extra complication (e.g. races with Py_DECREF()). It's simpler to make the mapping keep strong references to the map values.
|
Latest push fixes some crashes found when running a multi-threaded version of the grpc route_guide example. I avoided the need for |
If the module has been finalized, PyState_FindModule() will return NULL. Since we try to get the module state from various _Dealloc methods, we need to handle this case.
|
Any feedback on what might need to be done to move this PR along? Based on my (admittedly somewhat limited) testing, it seems to work. It shouldn't impact the non-free-threaded build since the alternative logic is either conditional on free-threading being enabled or is a no-op for the default build (like the locking functions). |
Free-threaded builds don't have a stable ABI. Use an ABI tag based on the CPython version number.
This is a bit more efficient. We handle obj_cache being NULL in the case of shutdown.
The logic to do this is complex but it is required to avoid leaking memory.
|
The free threading support is under discussion. We will also document what will be thread safe and what are not thread safe. Sharing messages are not thread safe usages we will support. We will submit some experimental free threading PRs for python cpp soon. UPB may take longer time for design, because message is under global object map. Add locks to each message is not what we want. |
This branch adds support for the free-threaded (nogil) build of CPython. It includes the work done by Lysandros (use strong references, critical sections for
Messageobjects). I excluded the changes to the "cpp" backend since I think we should focus on support for upb only at this time. These changes are designed such that they shouldn't affect the behavior or performance of the default (non-free-threaded) build.Lysandraos's PRs superseded by this one:
Messageobjects across threads #23056Additional changes I made:
threading.RLock()c_descriptor_symtabdata on module init, avoiding thread-safety issue.py_wheelfunction inpython/dist/BUILD.bazelso that free-threaded wheel files and .so files have to correct names (free-threaded build does not yet have a stable ABI).I have some limited multi-threaded testing that I've run with the TSAN build. These changes fix all TSAN warnings and the tests complete without crashing (previously they caused a SEGV).
I've also tested the multi-threaded scaling of the free-threading build of the modified library. Unfortunately, it doesn't scale well when used from multiple parallel threads. The ObjCache structure is a bottleneck since it is updated on every
Messagecreation and deletion. I think that could be fixed by having some lock-free hash map operatations for ObjCache (i.e. make the fast path where the object already exists not need to acquire the lock). It's not clear to me ifupb_inttable_*is thread-safe without locking. I think that scaling work could come later, after we have it working without crashing.Instructions on testing this:
--with-pydebugoptions on.Install the package:
python3 -m pip install --force-reinstall bazel-bin/python/dist/protobuf-6.33.0-cp314-cp314t-linux_x86_64.whlRun tests, e.g.
python3 pb_threaded.pyAttached is the
pb_threaded.pyscript I was using to test. It is too simple yet and we need some better testing. Perhaps we can use pytest-run-parallel and run the existing unit tests.pb_threaded.py.txt