Support free-threaded Python with a direct-threading backend
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 184
- Forks
- 9
- PR merge metrics
- No merged PRs in 30d
Description
Summary
Python 3.13+ ships an optional free-threaded build (--disable-gil) where the GIL is removed entirely. In free-threaded Python, plain threads already run in parallel — subinterpreters are no longer needed for parallelism, only isolation. bocpy should offer an optimized execution path for this runtime that avoids subinterpreter and XIData overhead while preserving BOC's deadlock-freedom guarantees.
This work is gated on the free-threaded ecosystem stabilizing. The free-threaded build is still experimental as of Python 3.15 and the subinterpreter/XIData APIs continue to evolve. We should not invest in a second backend until the APIs are stable and the free-threaded build is no longer opt-in.
Motivation
On free-threaded Python, bocpy's current architecture pays significant overhead for no parallelism benefit:
- XIData serialization/deserialization on every cown transfer (pickle round-trip for complex types)
- Subinterpreter lifecycle management (create, run_string, destroy per worker)
- Transpiler/AST export step to make closures importable across interpreters
- Module re-import in each worker interpreter
All of this machinery exists to work around the per-interpreter GIL. Without a GIL, workers can be plain threads operating directly on shared Python objects, and the cown/2PL protocol itself provides the necessary thread safety.
BOC's value proposition — deadlock and data-race freedom by construction — is arguably more valuable on free-threaded Python, where programmers face genuine shared-memory concurrency hazards that the GIL previously masked.
Design
At runtime, detect the threading model and select the appropriate backend:
import sys
if hasattr(sys, '_is_gil_enabled') and not sys._is_gil_enabled():
# Free-threaded: use direct threading
else:
# GIL build: use subinterpreters (current path)
Shared components (unchanged in either mode):
_core.cMPSC message queue (already lock-free C11 atomics)_core.c2PL scheduler andBOCBehavior/BOCCownrequest machinerybehaviors.pyBehaviorsscheduler threadCown[T]public API
Free-threaded mode changes:
- Workers become plain
threading.Threads running in the main interpreter - Cowns store
PyObject*directly instead of going through XIData — acquire/release usesPyMutexor equivalent - Behaviors execute closures directly — no transpiler, no AST export, no module re-import
_core.cinternal state protected withPy_BEGIN_CRITICAL_SECTIONwhere dicts/lists are accessed concurrently- The
BOCRecycleQueue(XIData GC) becomes unnecessary
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read _core.c and behaviors.py first, focusing on the existing scheduler, message queue, and BOCBehavior/BOCCown request machinery. Confirm how the runtime threading model is detected and how the current subinterpreter, XIData, and BOCRecycleQueue paths are connected. Done means a stable free-threaded backend can use direct threads while preserving BOC's deadlock- and data-race-freedom guarantees.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, python
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100