Skip to content

Conversation

@Yhg1s
Copy link

@Yhg1s Yhg1s commented Oct 27, 2025

Add support for free-threaded Python (PEP 703).

The significant change here is the use of thread local instead of a volatile
global for the switching_thread_state global (which is otherwise protected
by the GIL). There's some overhead to using a thread local, so only do this
in the free-threaded build.

The only other two bits of shared mutable data are G_TOTAL_MAIN_GREENLETS
and ThreadState::clocks_used_during_gc. Modify the latter to use a
std::atomic with relaxed memory order, which should be good enough, and
performance probably matters for those updates.

For G_TOTAL_MAIN_GREENLETS, switch to a std::atomic without changing the
inc/dec operations (which means they use sequential consistency), because
they're rare enough that performance doesn't really matter.

Also mark the main extension modules and the two test extensions as
supporting free-threading (without switching to multi-phase init). The GIL
will still temporarily be enabled during module import, but that probably
won't matter (modules are usually imported before starting threads). If it
does, switching to multi-phase init is always an option.

The existing test suite cover threads extensively enough that no extra tests
are necessary. There is an intermittent failure (<0.2% of runs) that shows
up when running the testsuite in a tight loop, but this happens in regular
Python builds (and before 3.14) too. ThreadSanitizer can't be used on
greenlet, from what I can tell because of how it gets confused by the stack
switching. This is the case for GILful Python builds as well.

Yhg1s added 2 commits October 27, 2025 14:34
The significant change here is the use of thread local instead of a volatile
global for the switching_thread_state global (which is otherwise protected
by the GIL). There's some overhead to using a thread local, so only do this
in the free-threaded build.

The only other two bits of shared mutable data are `G_TOTAL_MAIN_GREENLETS
and ThreadState::clocks_used_during_gc. Modify the latter to use a
std::atomic with relaxed memory order, which should be good enough, and
performance probably matters for those updates.

For G_MAIN_TOTAL_GREENLETS, switch to a std::atomic without changing the
inc/dec operations (which means they use sequential consistency), because
they're rare enough that performance doesn't really matter.

Also mark the main extension modules and the two test extensions as
supporting free-threading (without switching to multi-phase init). The GIL
will still temporarily be enabled during module import, but that probably
won't matter (modules are usually imported before starting threads). If it
does, switching to multi-phase init is always an option.

The existing test suite cover threads extensively enough that no extra tests
are necessary. There is an intermittent failure (<0.2% of runs) that shows
up when running the testsuite in a tight loop, but this happens in regular
Python builds (and before 3.14) too. ThreadSanitizer can't be used on
greenlet, from what I can tell because of how it gets confused by the stack
switching. This is the case for GILful Python builds as well.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants