Skip to content

feat: free-threading (nogil) support#108

Merged
lesnik512 merged 13 commits into
mainfrom
free-threading-support
Jul 18, 2026
Merged

feat: free-threading (nogil) support#108
lesnik512 merged 13 commits into
mainfrom
free-threading-support

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Certifies httpware under free-threaded CPython (PEP 703), backed by evidence rather than a bare classifier. Verify-first: the public claim (Free Threading :: 2 - Beta classifier) lands last, after the tests, CI, and benchmark prove it.

Full rationale and design: planning/changes/2026-07-18.01-nogil-free-threading-support.md.

What's here

  • pytest-freethreaded CI job on 3.14t (full suite + all extras from prebuilt wheels), asserting the GIL is disabled before the run. Regular GIL matrix (3.11–3.14) unchanged.
  • Real-thread stress tests (pytest.mark.stress) for the thread-shared components — sync Bulkhead, CircuitBreaker, and the shared RetryBudget — plus an httpx2 shared-pool boundary test. All pass on 3.14t with the GIL disabled. The async AsyncBulkhead/AsyncCircuitBreaker are single-event-loop bound (cross-loop use is rejected by the guard, deterministically tested) and not thread-parallel-stressed by design.
  • Contention benchmark (benchmarks/contention.py) + recorded baseline. Honest finding: free-threading is ~1.9× slower for the single-shared-lock RetryBudget workload — lock contention dominates. This is a correctness certification, not a speedup.
  • Free Threading :: 2 - Beta classifier and architecture/ promotion.

Scoped out

  • 3.13t is deferred (planning/deferred.md) — msgspec ships no cp313t free-threaded wheel, so 3.13t can't run the extras from wheels.

Verification

just lint-ci green; 783 tests at 100% coverage; all five stress-marked tests confirmed passing on real 3.14t with sys._is_gil_enabled() False.

🤖 Generated with Claude Code

lesnik512 and others added 13 commits July 18, 2026 08:48
Full-lane design for certifying httpware under free-threaded CPython
(PEP 703): verify-first sequencing, free-threaded CI on 3.14t (full
suite + all extras; 3.13t deferred on the msgspec cp313t wheel gap),
parallel stress tests for the Lock-based resilience components + the
shared httpx2 pool, a committed contention benchmark, and the Free
Threading classifier added last. Backed by empirical findings: httpx2
2.5.0 passes a 32-thread shared-pool stress run under nogil, and the
extras wheel matrix (pydantic/msgspec/otel) is verified per interpreter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hreading baseline

Adds benchmarks/contention.py, measuring shared-RetryBudget throughput under
real thread parallelism (GIL 3.11 vs free-threaded 3.14t); not a CI gate,
characterization only. Records the baseline numbers plus the project's
free-threading findings (extras wheel matrix, httpx2 shared-pool boundary,
stress-test coverage) in planning/audits/2026-07-18-free-threading-audit.md.
Ruff and ty are unpinned in pyproject and floated forward since Tasks 2-6
landed: isort now orders these files' imports differently, PLR2004 flags the
200 status literal, and ty's stricter override check flags log_message's
narrowed signature. Mechanical fixes only (import order, a named status
constant, a stdlib-matching signature) -- no behavior change, confirmed by
the full suite still passing at 100% coverage.
The boundary paragraph reported the initial exploratory scratch numbers
(32 threads, 3x12,800) rather than the committed test (16 threads x 100 =
1,600, single run). Describe the committed regression test accurately and
attribute the heavier design-time validation separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the Free Threading :: 2 - Beta classifier and promote the nogil
work into the living docs: architecture/resilience.md notes the
Lock-based components' free-threaded stress verification and the
honest ~1.9x contention-benchmark slowdown (correctness, not speed),
architecture/testing.md documents the stress marker convention, and
architecture/overview.md adds a supported-versions section. Records
the 3.13t msgspec-wheel deferral and ships 0.16.0 release notes.
The RetryBudget and event-loop-guard free-threading tests duplicated
existing coverage (test_retry_budget_threadsafety.py's two barrier-synced
thread tests; test_bulkhead.py::test_cross_loop_acquire_raises_runtimeerror).
Delete the two near-duplicate files and instead mark the existing RetryBudget
thread-safety tests @pytest.mark.stress, so the free-threading proof set is
labelled without redundant tests. Update the audit doc's test list and drop
the now-imprecise "four/five" counts from the change summary + release notes
(also scoping the release-notes stress claim to the sync + shared components,
matching the resilience.md correction). Full suite 783 passing at 100%.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lesnik512
lesnik512 merged commit 54848e2 into main Jul 18, 2026
7 checks passed
@lesnik512
lesnik512 deleted the free-threading-support branch July 18, 2026 09:39
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.

1 participant