Skip to content

torch_c on CPython 3.15t: builds, imports, keeps the GIL (#51 step 1) - #70

Merged
b-re-w merged 10 commits into
developfrom
feat/ft315
Oct 16, 2026
Merged

b-re-w merged 10 commits into
developfrom
feat/ft315

Conversation

@b-re-w

@b-re-w b-re-w commented Oct 4, 2026

Copy link
Copy Markdown
Member

Refs #51 (step 1 of the order set on the issue; the issue stays open).

Feature added

  • torch_c builds against CPython 3.15.0rc1+freethreaded: cargo build --release --no-default-features with PYO3_PYTHON set to the 3.15t interpreter. abi3 is now a default cargo feature, so the cp313-abi3 build is unchanged (cargo check --release passes).
  • In a 3.15t venv, the shim imports and computes: torch.ones(2)+1 gives [2., 2.] and _aten_implemented is present. Grade: reaches.

Defect fixed

  • pyo3 0.29 declares a module GIL-free by default. The first 3.15t build read sys._is_gil_enabled() == False before any global had been audited. #[pymodule(gil_used = true)] keeps the GIL until the audit and its thread tests land. 3.15t now re-enables the GIL and warns.

Test added

  • tests/api/test_freethreaded.py (2 tests, BOTH in the manifest). It skips by name unless TORCHNATIVE_FT_PYTHON and TORCHNATIVE_FT_STAGE are set. Run live on the host: 2 ok.
  • Nullified: rebuilt without gil_used = true, and test_module_keeps_the_gil_until_the_audit_lands went FAIL.

Not done (remaining #51 criteria)

  • cp315t wheels in publish-pypi.yml for the macOS, iOS, Android, Linux and Windows targets.
  • The full gate on 3.15t.
  • The thread audit and its hammer test.
  • Declaring Py_mod_gil.

abi3 becomes a default cargo feature; --no-default-features builds a
version-pinned _C against a free-threaded interpreter. pyo3 0.29 declares a
module GIL-free by default, and the first 3.15t build read
sys._is_gil_enabled() == False before any global was audited, so the module
now says gil_used = true. tests/api/test_freethreaded.py checks both in a
3.15t subprocess; with the attribute removed it goes red.
@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Gate on CI (issue #24)

Full gate: 126 suites = 118 run on at least one CI runner + 8 local-only. The local gate (tests/run.sh on the Mac) is still the only run that covers all 126.

runner exit ran not run here PASS PARTIAL UNVERIFIED (all skipped) FAIL missing/empty ledger
linux 0 118 8 86 30 2 0 0 suites=126/126 ok=1759 FAIL=0 SKIP=270 (printed and re-read
macos 0 118 8 104 12 2 0 0 suites=126/126 ok=1858 FAIL=0 SKIP=171 (printed and re-read
  • linux CI PLAN: runner=linux ran=118/126 not-run=8 (UNVERIFIED on this runner, local-only here): devices/coreml/test_ane_subgraph_components.py, devices/coreml/test_anedecode.py, devices/coreml/test_anesubgraph.py, devices/coreml/test_anetracer.py, devices/coreml/test_coremlops.py, gate/test_toolguard_run_preflight.py, release/test_toolguard_wheel_staging.py, release/test_wheelmatrix.py
  • linux VULKAN COVERAGE: UNVERIFIED -- no Vulkan test executed in this run. A green gate here says nothing about any Vulkan kernel.
  • linux DOCWATCH: PASS -- 1508/1508 evaluated marker(s) hold, 30 skipped
  • macos CI PLAN: runner=macos ran=118/126 not-run=8 (UNVERIFIED on this runner, local-only here): devices/coreml/test_ane_subgraph_components.py, devices/coreml/test_anedecode.py, devices/coreml/test_anesubgraph.py, devices/coreml/test_anetracer.py, devices/coreml/test_coremlops.py, gate/test_toolguard_run_preflight.py, release/test_toolguard_wheel_staging.py, release/test_wheelmatrix.py
  • macos VULKAN COVERAGE: UNVERIFIED -- no Vulkan test executed in this run. A green gate here says nothing about any Vulkan kernel.
  • macos DOCWATCH: PASS -- 1508/1508 evaluated marker(s) hold, 30 skipped

Local-only: verified by no CI runner (8)

  • devices/coreml/test_ane_subgraph_components.py -- imports coremltools at module scope, so without it the suite dies at import, and what it measures is subgraph placement on the Apple Neural Engine, which no hosted runner has. It also has no _skip runner of its own.
  • devices/coreml/test_anedecode.py -- needs CoreML's MLComputePlan answering for the Apple Neural Engine (AGENTS.md §13.3), which no hosted runner has; and its fixture reports a missing coremltools with a hand-rolled print followed by ok, so on a runner it would print ok having run nothing. Local-only until its skips go through _skip, and even then it would only skip.
  • devices/coreml/test_anesubgraph.py -- needs CoreML's MLComputePlan answering for the Apple Neural Engine (AGENTS.md §13.3), which no hosted runner has; and its fixture reports a missing coremltools with a hand-rolled print followed by ok, so on a runner it would print ok having run nothing. Local-only until its skips go through _skip, and even then it would only skip.
  • devices/coreml/test_anetracer.py -- needs CoreML's MLComputePlan answering for the Apple Neural Engine (AGENTS.md §13.3), which no hosted runner has; and its fixture reports a missing coremltools with a hand-rolled print followed by ok, so on a runner it would print ok having run nothing. Local-only until its skips go through _skip, and even then it would only skip.
  • devices/coreml/test_coremlops.py -- needs CoreML's MLComputePlan answering for the Apple Neural Engine (AGENTS.md §13.3), which no hosted runner has; and its fixture reports a missing coremltools with a hand-rolled print followed by ok, so on a runner it would print ok having run nothing. Local-only until its skips go through _skip, and even then it would only skip.
  • gate/test_toolguard_run_preflight.py -- needs the release machine's toolchain, which a hosted runner does not have: the pinned interpreter at .caches/spike-venv (transformers 5.15.1, AGENTS.md §15.2), the rust cross targets and the target CPython distributions under tools/wheel. Measured on run 37078300696 (macos): 3 of 3 tests FAIL with known-good interpreter missing: .../.caches/spike-venv/bin/python.
  • release/test_toolguard_wheel_staging.py -- needs the release machine's toolchain, which a hosted runner does not have: the pinned interpreter at .caches/spike-venv (transformers 5.15.1, AGENTS.md §15.2), the rust cross targets and the target CPython distributions under tools/wheel. Measured on run 37078300696 (macos): its one test FAILs with FileNotFoundError on the same interpreter.
  • release/test_wheelmatrix.py -- needs the release machine's toolchain, which a hosted runner does not have: the pinned interpreter at .caches/spike-venv (transformers 5.15.1, AGENTS.md §15.2), the rust cross targets and the target CPython distributions under tools/wheel. Measured on run 37078300696 (macos): 20 of 22 ok; test_every_target_names_a_rust_target_that_is_installed needs the nine cross targets the release machine has, and test_build_pys_own_self_test_passes asserts LINUX SELF-TEST: PASS, which build.py prints only where the target CPython distribution exists (it skips loudly otherwise). The 20 lose CI coverage until those two take the runner's toolchain into account; follow-up.

Suites that verified nothing, or failed, on a runner

suite runner verdict ok FAIL SKIP
api/test_freethreaded.py linux UNVERIFIED 0 0 5
devices/coreml/test_anepath.py linux UNVERIFIED 0 0 6
api/test_freethreaded.py macos UNVERIFIED 0 0 5
devices/coreml/test_anepath.py macos UNVERIFIED 0 0 6
Every suite, every runner
suite linux macos
_support/test_shim.py PARTIAL (462 ok / 0 FAIL / 18 SKIP) PARTIAL (472 ok / 0 FAIL / 8 SKIP)
api/test_freethreaded.py UNVERIFIED (0 ok / 0 FAIL / 5 SKIP) UNVERIFIED (0 ok / 0 FAIL / 5 SKIP)
api/test_import.py PASS (3 ok / 0 FAIL / 0 SKIP) PASS (3 ok / 0 FAIL / 0 SKIP)
api/test_threadstate.py PASS (4 ok / 0 FAIL / 0 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
api/test_tnnamespace.py PASS (4 ok / 0 FAIL / 0 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
api/test_tntransformers.py PASS (14 ok / 0 FAIL / 0 SKIP) PASS (14 ok / 0 FAIL / 0 SKIP)
bindings/test_aliasinc.py PASS (8 ok / 0 FAIL / 0 SKIP) PASS (8 ok / 0 FAIL / 0 SKIP)
bindings/test_argform.py PASS (13 ok / 0 FAIL / 0 SKIP) PASS (13 ok / 0 FAIL / 0 SKIP)
bindings/test_bfallback.py PASS (9 ok / 0 FAIL / 0 SKIP) PASS (9 ok / 0 FAIL / 0 SKIP)
bindings/test_bind2.py PASS (6 ok / 0 FAIL / 0 SKIP) PASS (6 ok / 0 FAIL / 0 SKIP)
bindings/test_bind3.py PASS (17 ok / 0 FAIL / 0 SKIP) PASS (17 ok / 0 FAIL / 0 SKIP)
bindings/test_bind4.py PASS (19 ok / 0 FAIL / 0 SKIP) PASS (19 ok / 0 FAIL / 0 SKIP)
bindings/test_bind5.py PASS (27 ok / 0 FAIL / 0 SKIP) PASS (27 ok / 0 FAIL / 0 SKIP)
bindings/test_bindings.py PASS (14 ok / 0 FAIL / 0 SKIP) PASS (14 ok / 0 FAIL / 0 SKIP)
bindings/test_bkeyset.py PASS (9 ok / 0 FAIL / 0 SKIP) PASS (9 ok / 0 FAIL / 0 SKIP)
bindings/test_convbackend.py PASS (3 ok / 0 FAIL / 0 SKIP) PASS (3 ok / 0 FAIL / 0 SKIP)
bindings/test_dispatch.py PASS (12 ok / 0 FAIL / 0 SKIP) PASS (12 ok / 0 FAIL / 0 SKIP)
bindings/test_metakey.py PASS (5 ok / 0 FAIL / 0 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
bindings/test_methodspell.py PASS (5 ok / 0 FAIL / 0 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
bindings/test_setitem.py PASS (11 ok / 0 FAIL / 0 SKIP) PASS (11 ok / 0 FAIL / 0 SKIP)
devices/coreml/test_ane_subgraph_components.py NOT RUN (0 ok / 0 FAIL / 1 SKIP) NOT RUN (0 ok / 0 FAIL / 1 SKIP)
devices/coreml/test_anedecode.py NOT RUN (0 ok / 0 FAIL / 1 SKIP) NOT RUN (0 ok / 0 FAIL / 1 SKIP)
devices/coreml/test_anepath.py UNVERIFIED (0 ok / 0 FAIL / 6 SKIP) UNVERIFIED (0 ok / 0 FAIL / 6 SKIP)
devices/coreml/test_anesubgraph.py NOT RUN (0 ok / 0 FAIL / 1 SKIP) NOT RUN (0 ok / 0 FAIL / 1 SKIP)
devices/coreml/test_anetracer.py NOT RUN (0 ok / 0 FAIL / 1 SKIP) NOT RUN (0 ok / 0 FAIL / 1 SKIP)
devices/coreml/test_bf16ane.py PARTIAL (1 ok / 0 FAIL / 20 SKIP) PARTIAL (1 ok / 0 FAIL / 20 SKIP)
devices/coreml/test_coremlops.py NOT RUN (0 ok / 0 FAIL / 1 SKIP) NOT RUN (0 ok / 0 FAIL / 1 SKIP)
devices/coreml/test_emptyplan.py PASS (17 ok / 0 FAIL / 0 SKIP) PASS (17 ok / 0 FAIL / 0 SKIP)
devices/coreml/test_npmarshal.py PARTIAL (6 ok / 0 FAIL / 1 SKIP) PARTIAL (6 ok / 0 FAIL / 1 SKIP)
devices/coreml/test_npu2.py PARTIAL (3 ok / 0 FAIL / 12 SKIP) PARTIAL (3 ok / 0 FAIL / 12 SKIP)
devices/cuda/test_cuda.py PASS (18 ok / 0 FAIL / 0 SKIP) PASS (18 ok / 0 FAIL / 0 SKIP)
devices/mps/test_absmps.py PARTIAL (2 ok / 0 FAIL / 2 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
devices/mps/test_constset.py PARTIAL (4 ok / 0 FAIL / 6 SKIP) PASS (10 ok / 0 FAIL / 0 SKIP)
devices/mps/test_gemmint.py PARTIAL (4 ok / 0 FAIL / 2 SKIP) PASS (6 ok / 0 FAIL / 0 SKIP)
devices/mps/test_i8mps.py PARTIAL (1 ok / 0 FAIL / 4 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
devices/mps/test_intmps.py PARTIAL (3 ok / 0 FAIL / 5 SKIP) PASS (8 ok / 0 FAIL / 0 SKIP)
devices/mps/test_metalcount.py PARTIAL (1 ok / 0 FAIL / 3 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
devices/mps/test_metalplace.py PARTIAL (1 ok / 0 FAIL / 4 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
devices/mps/test_mpsattn.py PARTIAL (4 ok / 0 FAIL / 6 SKIP) PASS (10 ok / 0 FAIL / 0 SKIP)
devices/mps/test_mpsconst.py PARTIAL (3 ok / 0 FAIL / 10 SKIP) PASS (13 ok / 0 FAIL / 0 SKIP)
devices/mps/test_mpsfwd.py PARTIAL (2 ok / 0 FAIL / 7 SKIP) PASS (9 ok / 0 FAIL / 0 SKIP)
devices/mps/test_mpsgen.py PARTIAL (2 ok / 0 FAIL / 5 SKIP) PASS (7 ok / 0 FAIL / 0 SKIP)
devices/mps/test_mpsinplace.py PARTIAL (1 ok / 0 FAIL / 8 SKIP) PASS (9 ok / 0 FAIL / 0 SKIP)
devices/mps/test_mpsrefuse.py PARTIAL (4 ok / 0 FAIL / 2 SKIP) PASS (6 ok / 0 FAIL / 0 SKIP)
devices/mps/test_viewdtype.py PARTIAL (5 ok / 0 FAIL / 1 SKIP) PASS (6 ok / 0 FAIL / 0 SKIP)
devices/npu/test_intelnpu.py PARTIAL (43 ok / 0 FAIL / 7 SKIP) PARTIAL (43 ok / 0 FAIL / 7 SKIP)
devices/npu/test_npublob.py PASS (13 ok / 0 FAIL / 0 SKIP) PASS (13 ok / 0 FAIL / 0 SKIP)
devices/npu/test_npudim.py PASS (8 ok / 0 FAIL / 0 SKIP) PASS (8 ok / 0 FAIL / 0 SKIP)
devices/npu/test_npufuse.py PARTIAL (11 ok / 0 FAIL / 3 SKIP) PARTIAL (11 ok / 0 FAIL / 3 SKIP)
devices/npu/test_npuvendor.py PASS (17 ok / 0 FAIL / 0 SKIP) PASS (17 ok / 0 FAIL / 0 SKIP)
devices/npu/test_npuwire.py PASS (12 ok / 0 FAIL / 0 SKIP) PASS (12 ok / 0 FAIL / 0 SKIP)
devices/npu/test_ovcache.py PASS (12 ok / 0 FAIL / 0 SKIP) PASS (12 ok / 0 FAIL / 0 SKIP)
devices/npu/test_ovpar.py PASS (14 ok / 0 FAIL / 0 SKIP) PASS (14 ok / 0 FAIL / 0 SKIP)
devices/qnn/test_qnn.py PARTIAL (15 ok / 0 FAIL / 12 SKIP) PARTIAL (16 ok / 0 FAIL / 11 SKIP)
devices/qnn/test_qnn_ops.py PASS (10 ok / 0 FAIL / 0 SKIP) PASS (10 ok / 0 FAIL / 0 SKIP)
devices/qnn/test_qnn_plan.py PASS (12 ok / 0 FAIL / 0 SKIP) PASS (12 ok / 0 FAIL / 0 SKIP)
devices/qnn/test_qnnci.py PASS (30 ok / 0 FAIL / 0 SKIP) PASS (30 ok / 0 FAIL / 0 SKIP)
devices/qnn/test_qnnprobe.py PASS (4 ok / 0 FAIL / 0 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
devices/test_devicens.py PASS (25 ok / 0 FAIL / 0 SKIP) PASS (25 ok / 0 FAIL / 0 SKIP)
devices/test_dtmdev.py PARTIAL (1 ok / 0 FAIL / 4 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
devices/test_dtypedev.py PARTIAL (6 ok / 0 FAIL / 5 SKIP) PASS (11 ok / 0 FAIL / 0 SKIP)
devices/vulkan/test_vulkan4.py PARTIAL (5 ok / 0 FAIL / 71 SKIP) PARTIAL (5 ok / 0 FAIL / 71 SKIP)
devices/vulkan/test_vulkancov.py PASS (8 ok / 0 FAIL / 0 SKIP) PASS (8 ok / 0 FAIL / 0 SKIP)
distributed/test_asyncwork.py PASS (9 ok / 0 FAIL / 0 SKIP) PASS (9 ok / 0 FAIL / 0 SKIP)
distributed/test_collect2.py PASS (16 ok / 0 FAIL / 0 SKIP) PASS (16 ok / 0 FAIL / 0 SKIP)
distributed/test_gloopin.py PASS (6 ok / 0 FAIL / 0 SKIP) PASS (6 ok / 0 FAIL / 0 SKIP)
export/test_export.py PASS (15 ok / 0 FAIL / 0 SKIP) PASS (15 ok / 0 FAIL / 0 SKIP)
export/test_export4.py PASS (18 ok / 0 FAIL / 0 SKIP) PASS (18 ok / 0 FAIL / 0 SKIP)
export/test_export5.py PASS (21 ok / 0 FAIL / 0 SKIP) PASS (21 ok / 0 FAIL / 0 SKIP)
export/test_export6.py PASS (11 ok / 0 FAIL / 0 SKIP) PASS (11 ok / 0 FAIL / 0 SKIP)
export/test_liftfresh.py PASS (9 ok / 0 FAIL / 0 SKIP) PASS (9 ok / 0 FAIL / 0 SKIP)
export/test_structseq.py PASS (7 ok / 0 FAIL / 0 SKIP) PASS (7 ok / 0 FAIL / 0 SKIP)
export/test_varmean.py PASS (7 ok / 0 FAIL / 0 SKIP) PASS (7 ok / 0 FAIL / 0 SKIP)
gate/test_benchguard_ab_upstream.py PASS (6 ok / 0 FAIL / 0 SKIP) PASS (6 ok / 0 FAIL / 0 SKIP)
gate/test_gatelock.py PASS (10 ok / 0 FAIL / 0 SKIP) PASS (10 ok / 0 FAIL / 0 SKIP)
gate/test_skipvis.py PASS (4 ok / 0 FAIL / 0 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
gate/test_split_probe.py PASS (2 ok / 0 FAIL / 0 SKIP) PASS (2 ok / 0 FAIL / 0 SKIP)
gate/test_tmpleak.py PASS (3 ok / 0 FAIL / 0 SKIP) PASS (3 ok / 0 FAIL / 0 SKIP)
gate/test_toolguard_run_preflight.py NOT RUN (0 ok / 0 FAIL / 1 SKIP) NOT RUN (0 ok / 0 FAIL / 1 SKIP)
golden/test_x86pins.py PASS (5 ok / 0 FAIL / 0 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
models/test_cbpath.py PASS (5 ok / 0 FAIL / 0 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
models/test_cbwalls.py PARTIAL (9 ok / 0 FAIL / 6 SKIP) PARTIAL (14 ok / 0 FAIL / 1 SKIP)
models/test_higgs.py PASS (23 ok / 0 FAIL / 0 SKIP) PASS (23 ok / 0 FAIL / 0 SKIP)
models/test_qwen3.py PARTIAL (9 ok / 0 FAIL / 13 SKIP) PARTIAL (12 ok / 0 FAIL / 10 SKIP)
models/test_stream.py PARTIAL (6 ok / 0 FAIL / 9 SKIP) PARTIAL (9 ok / 0 FAIL / 6 SKIP)
models/test_voice3.py PARTIAL (37 ok / 0 FAIL / 2 SKIP) PASS (39 ok / 0 FAIL / 0 SKIP)
models/test_voice4.py PARTIAL (12 ok / 0 FAIL / 2 SKIP) PARTIAL (12 ok / 0 FAIL / 2 SKIP)
numerics/test_agree.py PASS (16 ok / 0 FAIL / 0 SKIP) PASS (16 ok / 0 FAIL / 0 SKIP)
numerics/test_agree2.py PASS (13 ok / 0 FAIL / 0 SKIP) PASS (13 ok / 0 FAIL / 0 SKIP)
numerics/test_int8.py PASS (15 ok / 0 FAIL / 0 SKIP) PASS (15 ok / 0 FAIL / 0 SKIP)
numerics/test_remeasure2.py PASS (4 ok / 0 FAIL / 0 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
ops/test_canine.py PASS (4 ok / 0 FAIL / 0 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
ops/test_complex.py PASS (12 ok / 0 FAIL / 0 SKIP) PASS (12 ok / 0 FAIL / 0 SKIP)
ops/test_cplx2.py PASS (10 ok / 0 FAIL / 0 SKIP) PASS (10 ok / 0 FAIL / 0 SKIP)
ops/test_equal_allclose.py PASS (21 ok / 0 FAIL / 0 SKIP) PASS (21 ok / 0 FAIL / 0 SKIP)
ops/test_fft.py PASS (16 ok / 0 FAIL / 0 SKIP) PASS (16 ok / 0 FAIL / 0 SKIP)
ops/test_glu.py PASS (5 ok / 0 FAIL / 0 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
ops/test_indexsel.py PASS (3 ok / 0 FAIL / 0 SKIP) PASS (3 ok / 0 FAIL / 0 SKIP)
ops/test_last7.py PASS (17 ok / 0 FAIL / 0 SKIP) PASS (17 ok / 0 FAIL / 0 SKIP)
ops/test_metaemb.py PASS (20 ok / 0 FAIL / 0 SKIP) PASS (20 ok / 0 FAIL / 0 SKIP)
ops/test_metafam.py PASS (15 ok / 0 FAIL / 0 SKIP) PASS (15 ok / 0 FAIL / 0 SKIP)
ops/test_metastride.py PASS (13 ok / 0 FAIL / 0 SKIP) PASS (13 ok / 0 FAIL / 0 SKIP)
ops/test_pad.py PASS (23 ok / 0 FAIL / 0 SKIP) PASS (23 ok / 0 FAIL / 0 SKIP)
ops/test_repeat.py PASS (18 ok / 0 FAIL / 0 SKIP) PASS (18 ok / 0 FAIL / 0 SKIP)
ops/test_rnn.py PASS (26 ok / 0 FAIL / 0 SKIP) PASS (26 ok / 0 FAIL / 0 SKIP)
ops/test_scatter.py PASS (23 ok / 0 FAIL / 0 SKIP) PASS (23 ok / 0 FAIL / 0 SKIP)
ops/test_strided.py PASS (12 ok / 0 FAIL / 0 SKIP) PASS (12 ok / 0 FAIL / 0 SKIP)
ops/test_tail1.py PASS (32 ok / 0 FAIL / 0 SKIP) PASS (32 ok / 0 FAIL / 0 SKIP)
ops/test_tail2.py PASS (11 ok / 0 FAIL / 0 SKIP) PASS (11 ok / 0 FAIL / 0 SKIP)
ops/test_tail3.py PASS (17 ok / 0 FAIL / 0 SKIP) PASS (17 ok / 0 FAIL / 0 SKIP)
ops/test_tail4.py PASS (25 ok / 0 FAIL / 0 SKIP) PASS (25 ok / 0 FAIL / 0 SKIP)
ops/test_vmap.py PASS (8 ok / 0 FAIL / 0 SKIP) PASS (8 ok / 0 FAIL / 0 SKIP)
release/test_cigate.py PASS (19 ok / 0 FAIL / 0 SKIP) PASS (19 ok / 0 FAIL / 0 SKIP)
release/test_cipub.py PASS (38 ok / 0 FAIL / 0 SKIP) PASS (38 ok / 0 FAIL / 0 SKIP)
release/test_docrefs.py PASS (11 ok / 0 FAIL / 0 SKIP) PASS (11 ok / 0 FAIL / 0 SKIP)
release/test_ftwheel.py PASS (26 ok / 0 FAIL / 0 SKIP) PASS (26 ok / 0 FAIL / 0 SKIP)
release/test_layout.py PASS (6 ok / 0 FAIL / 0 SKIP) PASS (6 ok / 0 FAIL / 0 SKIP)
release/test_platver_harnesses.py PASS (9 ok / 0 FAIL / 0 SKIP) PASS (9 ok / 0 FAIL / 0 SKIP)
release/test_publish.py PASS (4 ok / 0 FAIL / 0 SKIP) PASS (4 ok / 0 FAIL / 0 SKIP)
release/test_release.py PARTIAL (10 ok / 0 FAIL / 1 SKIP) PASS (11 ok / 0 FAIL / 0 SKIP)
release/test_style.py PASS (5 ok / 0 FAIL / 0 SKIP) PASS (5 ok / 0 FAIL / 0 SKIP)
release/test_toolguard_wheel_staging.py NOT RUN (0 ok / 0 FAIL / 1 SKIP) NOT RUN (0 ok / 0 FAIL / 1 SKIP)
release/test_wheelmatrix.py NOT RUN (0 ok / 0 FAIL / 1 SKIP) NOT RUN (0 ok / 0 FAIL / 1 SKIP)
training/test_stage0.py PASS (12 ok / 0 FAIL / 0 SKIP) PASS (12 ok / 0 FAIL / 0 SKIP)
training/test_stagetype.py PASS (6 ok / 0 FAIL / 0 SKIP) PASS (6 ok / 0 FAIL / 0 SKIP)
training/test_train.py PASS (11 ok / 0 FAIL / 0 SKIP) PASS (11 ok / 0 FAIL / 0 SKIP)

A SKIP is unverified on that runner, never passed. UNVERIFIED means the suite ran and every one of its tests skipped by name.

b-re-w added 2 commits October 5, 2026 18:05
build.py --abi ft builds _C with --no-default-features against a 3.15t
target interpreter and tags the wheel from its sysconfig (ftabi.py).
Windows, iOS, Android and wasm refuse by name: no free-threaded target
CPython, or a Windows path written against python313. publish-pypi gains a
build-ft job (host, linux-x86_64, linux-aarch64) on PBS 3.15.0rc1
freethreaded; collect expects those three wheels by name.
test_ftwheel 25 ok; GIL-check and tag mutants go red.
Dry run 37287742911 built both Linux cp315t wheels and then refused them:
verify() remapped `expected` for ft but still searched the archive for the
target's abi3 member. The host passed only because its target is None.
test_verify_finds_the_ft_member_for_a_cross_target was red on the old code.
b-re-w added 5 commits October 5, 2026 21:18
…am's are (#76)

Both were process-wide: torch.no_grad() in one thread stopped grad_fn
recording in another, and torch.device('meta') on one thread moved another
thread's factories to meta. Wrong with the GIL on, not only on 3.15t.
tensor.rs GRAD_ENABLED is a thread_local Cell; the bootstrap's grad flag and
_MODE_STACK sit on threading.local. tests/api/test_threadstate.py: 3 tests,
green on upstream torch, red on the shim before, green after.
… out from under a live draw (#76)

Streams were Box::leak'ed and reclaimed by Box::from_raw in stream_free, sound
only while a kernel's guard and the generator's __del__ could not overlap
under the GIL. stream() now returns a GenGuard that holds its own Arc clone;
stream_free drops the table's reference. The new cargo test frees a locked
stream and reuses its memory: SIGSEGV on the old code, green now.
…; test_threadstate sets TORCH_USE_RTLD_GLOBAL

CAPTURING was cleared by whichever thread ended its capture first, so a thread
still mid-capture stopped recording and its later ops vanished from its trace.
It is now an AtomicUsize of open regions (one per thread at most, since
capture_begin refuses nesting); the door still pays one relaxed load.

test_threadstate.py never ran in the gate before this commit: it died at
import torch for want of TORCH_USE_RTLD_GLOBAL, which every other suite that
imports torch sets. Now 4 ok in the gate (2010 -> 2014).
… mode (#51, #76)

8 threads behind a Barrier in a 3.15t subprocess that overrides gil_used.
Against a build without #76's fixes, grad and mode go red ('another thread's
no_grad applied here', 'pop from an empty torch function mode stack'); with
them all three are green. The RNG case stays green on the old code too: the
use-after-free is proven by the cargo test, not by this hammer.

The first version collected errors under the wrapper's name and so could
never fail; the nullification above is how that was found.
Also git-ignores target-ft/, build.py --abi ft's default cargo dir.
@b-re-w

b-re-w commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

This branch now also contains #77 (merged in locally so the 3.15t hammer could exercise its fixes), plus the hammer in tests/api/test_freethreaded.py:

@b-re-w

b-re-w commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

CI evidence (run 37321276340, d1f265e):

  • macOS: the gate itself passed (suites=125/125 ok=1853 FAIL=0, DOCWATCH: PASS 1508/1508, EXIT=0). The job then failed on ##[error]Failed to CreateArtifact: Unable to make request: ENOTFOUND, a network error uploading the log, not a test. I re-ran that job.
  • linux: FAIL=0; it fails only on DOCWATCH golden_cases_failed = 54 (Linux x86_64: the golden harness dies with SIGFPE (compare.py exit -8) #69).
  • report: failed because linux did.

@b-re-w

b-re-w commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

Paused (user's instruction). The macOS job was re-run on run 37321276340 after an ENOTFOUND artifact upload. Remaining #51 steps: the 9 'unclear' audit items, the full gate on 3.15t (blocked: no upstream torch build for 3.15t to compare against), and Py_mod_gil.

PBS release 20261013 ships 3.15.0 freethreaded for aarch64-apple-darwin and
both linux-gnu triples. The ft rows now fetch from their own PBS_FT_RELEASE so
the cp313-abi3 interpreter pin (PBS_RELEASE 20260825, 3.13.15) stays where
the published 0.1.0b5 wheels were built. test_ftwheel now requires 3.15.0
final and no 3.15 pre-release anywhere; it was red on the rc1 pin.
Locally: _C built against PBS 3.15.0t, tests/api/test_freethreaded.py 5/5 ok
(including the PYTHON_GIL=0 hammer).
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