P7. Release the GIL around execution
- Dominant language
- Mojo
- Stars
- 1
- Forks
- 0
- Avg merge
- 1h 31m
- Merged PRs (30d)
- 640
Description
Specification: [`07-python-bindings.md`](../blob/main/docs/specs/07-python-bindings.md) section 6, [`12-the-python-front-door-measured.md`](../blob/main/docs/specs/12-the-python-front-door-measured.md) section 4.
Release the GIL around execution and reacquire it to build the return value. The whole point is parallel execution in a language that does not have a global lock, and holding the lock while running a thirty second query would block every other thread in the user's process.
### The mechanism is confirmed
`std.python._cpython` exports `GILReleased` and it is a context manager:
```mojo
from std.python._cpython import GILReleased
with GILReleased(Python()):
...
```
Modular uses it in shipping code, in `max/_distributed_ops/distributed_ops.mojo` and `max/_distributed_ops/block_offload_ops.mojo`, with comments saying exactly what document 07 says.
It was measured rather than assumed. A Mojo function sleeping for one second while another Python thread increments a counter: holding the GIL, the other thread ticked once. Inside `GILReleased`, 673 times.
The first attempt at that measurement was worthless and the reason belongs in this issue because the same trap is waiting in every benchmark. The workload was an arithmetic loop over a hundred million integers, the compiler folded it to a constant, and the function returned the right answer in zero seconds so both configurations looked identical. A workload the optimiser can see through measures nothing.
### The rule that is easy to get wrong
**Do not touch a `PythonObject` from a worker thread.** Any Python callable in a user defined function is invoked from the calling thread, serially, or under an explicit reacquire. Getting this wrong produces an interpreter crash rather than an exception, which makes it the single most likely source of a hard to diagnose bug in this layer, and a crash in somebody else's process is the worst failure mode this project has.
### Scope
- [ ] `GILReleased` around every entry point that executes rather than inspects, chosen by a rule rather than one at a time
- [ ] A test asserting another Python thread makes progress during a long firepanda operation, using a workload the compiler cannot fold
- [ ] The worker thread rule stated in the module docstring where the execution layer meets the binding layer, since it is a rule about code that has not been written yet
- [ ] A test that a user defined function holding a Python callable runs it on the calling thread
- [ ] Whatever check is available that no `PythonObject` is constructed inside a `GILReleased` block, even if that check is a grep in CI
Contributor guide
Research direction
Read docs/specs/07-python-bindings.md section 6 and docs/specs/12-the-python-front-door-measured.md section 4, then locate the execution entry points in the Python binding layer. Use std.python._cpython.GILReleased and the shipping examples in max/_distributed_ops/distributed_ops.mojo and block_offload_ops.mojo as references. Done means the scoped entry points release the GIL safely, long operations let another Python thread progress, Python callables stay on the calling thread, and the documented safety check is present.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100