Repository navigation
CPython: declare the module GIL-free on free-threaded builds - #36
Merged
Merged
Conversation
PyInit_lvgl calls PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED) after PyModule_Create, guarded by Py_GIL_DISABLED, so importing lvgl on 3.13t and later no longer turns the GIL back on. LVGL isn't thread-safe: this holds because every lvgl call, and every lvgl object's release, happens on one thread.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On a free-threaded Python (3.13t and later), importing an extension that doesn't declare itself GIL-free turns the GIL back on for the whole process, with a RuntimeWarning. lvgl did, so a program on 3.15t lost its parallel threads the moment it imported lvgl.
PyInit_lvglnow callsPyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED)right afterPyModule_Create(binding/emit_cpython_glue.py), guarded byPy_GIL_DISABLEDso every other build compiles the same source unchanged.The rule that makes this safe: LVGL isn't thread-safe, and the CPython runtime keeps unlocked shared state of its own (static scratch structs for struct conversion, the callback-dict registry and the pending-release array, both grown with
realloc, and an LVGL lock released while Python callbacks run). So every lvgl call, and every lvgl object's release, has to happen on one thread. Other threads can run any Python that doesn't touch lvgl, in parallel. lvgl-python documents this for users and tests it on 3.15t: frames drawn on one thread while other threads run Python match frames drawn alone.generated/lvgl_python.cis regenerated (6 lines).tests/test_cpython_gil_declaration.pychecks the declaration in the generated module init; it fails with the guard planted wrong (since undone), and the generator suite passes (107 tests).Releasing it needs nothing new: lvgl-python's usual sync to this commit carries the declaration, and its cp315t wheels pick it up.