Skip to content

gh-153176: Fix destroy_interpreter() test helper clearing a non-current thread state#153307

Open
zangjiucheng wants to merge 3 commits into
python:mainfrom
zangjiucheng:gh-153176-fix
Open

gh-153176: Fix destroy_interpreter() test helper clearing a non-current thread state#153307
zangjiucheng wants to merge 3 commits into
python:mainfrom
zangjiucheng:gh-153176-fix

Conversation

@zangjiucheng

@zangjiucheng zangjiucheng commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #153176.

On free-threaded debug builds, _testinternalcapi.destroy_interpreter(id, basic=True)
aborted the process with:

Assertion `tstate == (_PyThreadStateImpl *)_PyThreadState_GET()' failed.

The root cause is in the test helper, not in obmalloc. destroy_interpreter()
calls PyThreadState_Clear(t1) / PyThreadState_Delete(t1) while t2 is the
current thread state. Clearing a non-current thread state runs
_PyThreadState_ClearMimallocHeaps(t1), and on a debug build
mi_heap_collect_ex() treats MI_ABANDON as >= MI_FORCE, so it calls
_mi_abandoned_reclaim_all() and reclaims abandoned pages back into t1's
heaps. That reaches _PyMem_mi_page_reclaimed(), where the reclaimed page
belongs to t1 but the current thread state is t2, so the assertion fires.

The assertion is correct — reclaim should only happen into the current thread's
own heap — so this keeps it and fixes the helper instead: t1 is left for
Py_EndInterpreter() to clean up, matching the rule that PyThreadState_Clear()
must be called on the current thread. Thanks @kumaraditya303 for pinpointing the
root cause.

Test plan

  • ./python -m test test_free_threading.test_interpreters -v
  • With the assertion in place, reproduced the abort on a free-threaded
    debug build before the fix; confirmed it no longer reproduces after.

@weixlu

weixlu commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

I also think we can just relax this assertion. Thanks for your fix!

Comment thread Objects/obmalloc.c Outdated
…ent thread state

_testinternalcapi.destroy_interpreter(basic=True) called PyThreadState_Clear()
and PyThreadState_Delete() on t1 while t2 was the current thread state. On a
free-threaded debug build this reclaimed mimalloc pages into a heap not owned
by the current thread, tripping the assertion in _PyMem_mi_page_reclaimed().

Leave t1 for Py_EndInterpreter() to clean up. Add a regression test.

Co-authored-by: Kumar Aditya <kumaraditya@python.org>
Co-authored-by: Xiaowei Lu <weixlu420302@gmail.com>
@kumaraditya303

Copy link
Copy Markdown
Contributor

The PR title and description needs to be updated.

@@ -0,0 +1,3 @@
Fix ``_testinternalcapi.destroy_interpreter()`` calling

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

News entry is not needed because it is test code.

@zangjiucheng zangjiucheng changed the title gh-153176: Fix crash in _PyMem_mi_page_reclaimed on free-threaded debug builds gh-153176: Fix destroy_interpreter() test helper clearing a non-current thread state Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assertion triggered in MiMalloc (free-threaded build)

4 participants