docs/testing-free-threading.md

# Testing with Free-Threaded Python

This guide explains how to test erlang_python with Python's experimental free-threading mode (no GIL).

## Overview

Python 3.13+ can be built with `--disable-gil` to create a "free-threaded" build where the Global Interpreter Lock is removed. This allows true parallel execution of Python code across multiple threads.

erlang_python automatically detects free-threaded Python and uses direct execution (no executor thread) for maximum performance.

## Installing Free-Threaded Python

### Option 1: Using pyenv (Recommended)

```bash
# Install pyenv if not already installed
curl https://pyenv.run | bash

# Install free-threaded Python 3.13+
PYTHON_CONFIGURE_OPTS="--disable-gil" pyenv install 3.13.0

# Or for 3.14+
PYTHON_CONFIGURE_OPTS="--disable-gil" pyenv install 3.14.0
```

### Option 2: Build from Source

```bash
# Download Python source
wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0.tar.xz
tar xf Python-3.13.0.tar.xz
cd Python-3.13.0

# Configure with --disable-gil
./configure --prefix=$HOME/python-nogil --disable-gil --enable-optimizations

# Build and install
make -j$(nproc)
make install
```

### Option 3: macOS with Homebrew

```bash
# Check if free-threading option is available
brew info python@3.13

# If available:
brew install python@3.13 --with-freethreading
```

### Option 4: Using Docker

```dockerfile
FROM python:3.13-rc

# Python RC images may include free-threading
# Check with: python -c "import sys; print(hasattr(sys, '_is_gil_enabled'))"
```

## Verifying Free-Threading is Enabled

```bash
# Check if GIL is disabled
python3 -c "
import sys
if hasattr(sys, '_is_gil_enabled'):
    print('Free-threading supported:', not sys._is_gil_enabled())
else:
    print('Free-threading not available (Python < 3.13 or GIL enabled)')
"
```

Expected output for free-threaded Python:
```
Free-threading supported: True
```

## Building erlang_python for Free-Threading

```bash
# Clean previous builds
rebar3 clean

# Set Python config path (adjust for your installation)
export PYTHON_CONFIG=$HOME/python-nogil/bin/python3-config

# Or if using pyenv
export PYTHON_CONFIG=$(pyenv prefix 3.13.0)/bin/python3-config

# Build
rebar3 compile
```

## Verifying Free-Threading Mode

```erlang
1> application:ensure_all_started(erlang_python).
{ok, [erlang_python]}

2> py:execution_mode().
free_threaded  % Should show 'free_threaded' instead of 'subinterp' or 'multi_executor'

3> py:num_executors().
1  % In free_threaded mode, no executor pool is used
```

## Running Tests

```bash
# Run the test suite
rebar3 ct --suite=py_SUITE

# Run benchmarks
escript examples/benchmark.erl --full
```

## Performance Comparison

Run benchmarks with both standard and free-threaded Python to compare:

### Standard Python (with GIL)
```bash
# Use standard Python
export PYTHON_CONFIG=/usr/bin/python3-config
rebar3 clean && rebar3 compile
escript examples/benchmark.erl --concurrent
```

### Free-Threaded Python
```bash
# Use free-threaded Python
export PYTHON_CONFIG=$HOME/python-nogil/bin/python3-config
rebar3 clean && rebar3 compile
escript examples/benchmark.erl --concurrent
```

Expected results:
- Free-threaded mode should show higher throughput for concurrent CPU-bound workloads
- The difference is most noticeable with many concurrent processes making Python calls

## Caveats

### Extension Compatibility

Not all Python C extensions are compatible with free-threading. Extensions that rely on the GIL for thread safety may crash or produce incorrect results.

**Known compatible:**
- Standard library modules
- NumPy (recent versions)

**May have issues:**
- Older C extensions
- Extensions using non-thread-safe C libraries

### Memory Model

Free-threaded Python uses a different memory model. Be aware of:
- Increased memory usage (per-object locks)
- Different garbage collection behavior
- Potential for data races in Python code

### Testing Recommendations

1. **Start with unit tests**: Ensure basic functionality works
2. **Test concurrency**: Run concurrent benchmarks
3. **Check for crashes**: Monitor for segfaults during heavy load
4. **Profile memory**: Watch for memory leaks or bloat

## Troubleshooting

### Build Fails

```
error: Python.h not found
```

Ensure `PYTHON_CONFIG` points to the free-threaded Python installation:
```bash
ls $(dirname $(which python3))/../include/*/Python.h
```

### Mode Shows 'multi_executor' Instead of 'free_threaded'

The Python build may not have `Py_GIL_DISABLED` defined. Verify:
```bash
python3 -c "import sysconfig; print(sysconfig.get_config_var('Py_GIL_DISABLED'))"
```

Should print `1` for free-threaded builds.

### Crashes Under Load

Some extensions may not be thread-safe. Try:
1. Isolate the problematic extension
2. Check if a thread-safe version exists
3. Fall back to sub-interpreter mode for those calls

## See Also

- [PEP 703 - Making the GIL Optional](https://peps.python.org/pep-0703/)
- [Python Free-Threading Documentation](https://docs.python.org/3.13/howto/free-threading-python.html)
- [Scalability Guide](scalability.md)