Skip to content

Add the concurrency stress test that reproduces #175 - #176

Merged
HugoFara merged 1 commit into
devfrom
test/threading-stress
Aug 12, 2026
Merged

Add the concurrency stress test that reproduces #175#176
HugoFara merged 1 commit into
devfrom
test/threading-stress

Conversation

@HugoFara

Copy link
Copy Markdown
Collaborator

The reproduction harness for #175, plus the documentation for running it. No changes to src/.

Eight threads, each building a 1000×1000 domain with the Iso propagation model, igniting at the centre and stepping five times. Each thread must reproduce the fire node count it produces when run alone. A second case hammers object construction, which draws every id from one shared counter.

What it measures

Built from source against CPython 3.14.6 free-threading in a manylinux_2_28 container:

BuildSame 8 threads, GIL onGIL off, 5 runs
dev @ e1df8873/3 pass4 × segfault, 1 × wrong results
dev + the small fixes proposed in #1754 × segfault, 1 × abort
full free-threading branchpass5/5 pass

The GIL-on column is the control: identical workload, identical code, passing every time. The only variable is whether the calls are serialised.

The one crash-free run on dev is the interesting one:

single-threaded baseline: 62 fire nodes
FAILED with 7 problem(s):
- thread 1 produced 49 fire nodes, expected 62
- thread 3 produced 209 fire nodes, expected 62
- thread 4 produced 27 fire nodes, expected 62

Wrong answers rather than a crash, which is the worse failure of the two.

Deliberately not wired into CI

On an ordinary interpreter the GIL serialises every call into the extension, so the test skips and exits 0. A CI job would sit green while proving nothing — the same failure mode as a warning nobody reads. It earns a job at the end of #175, once it can pass for the right reason. Please do not add one as a follow-up before then.

PYTHON_GIL=0 is required rather than optional: _pyforefire does not declare py::mod_gil_not_used(), so importing it switches the GIL back on, and the test would otherwise skip on a free-threaded interpreter too. It says so when that happens rather than passing quietly.

What this does not do

It does not fix anything. It fails on dev, and it is meant to. Merging it makes the problem in #175 reproducible on demand; the fixes are separate pull requests, and by the measurements above only the full state refactor makes it pass.

TESTING.md gains a section covering the interpreter requirement, the invocation, and the fact that it currently fails.


This pull request, including its code changes and this description, was generated by Claude Opus 5, and reviewed manually before submitting.

Eight simulations in threads, each required to reproduce the fire node
count it produces when run alone, plus a case hammering object
construction to exercise the shared id counter.
Skips when the GIL is on, since every call into the extension is
serialised there and no race can show. Under PYTHON_GIL=0 on a
free-threaded interpreter it segfaults or returns wrong counts, which is
the point: it gives the shared-state work in #175 a failing test to fix.
Deliberately not wired into CI. On an ordinary interpreter it would skip
and report green while proving nothing; it earns a job once it can pass
for the right reason.
Refs #175
@HugoFara

Copy link
Copy Markdown
CollaboratorAuthor

Test/doc only, merging myself.

@HugoFara
HugoFara merged commit 411190f into devAug 12, 2026
3 checks passed
@antonio-leblanc
antonio-leblanc deleted the test/threading-stress branch August 13, 2026 02:30
Sign up for freeto 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

@HugoFara