Repository navigation
fix: ExitProcess hang on Windows (2.2.7) - #56
Merged
Merged
Conversation
ExitProcess terminates every other thread wherever it stands, then runs the exiting thread's FLS callbacks, thread_done_cb among them. A thread killed while holding HEAPS_LOCK (a pool worker in its own teardown) left heaps_unregister spinning forever, in a process TerminateProcess cannot end. ArtCraft hit it 2 times in ~1,060 runs; thread_done_cb+0x6e2 in their binary is the heaps_lock spin. thread_done_cb now returns when RtlDllShutdownInProgress() is true: nothing can adopt the heap and the address space is about to go. tests/exit_hang.rs parks a thread inside for_each_heap (HEAPS_LOCK held) and exits the process; it hung on 2.2.6 and exits in under a second now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cut from the 2.2.6 tag so the unreleased speed work on main does not ship with it, the same way 2.2.6 was cut from 2.2.5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ttimmahlax
pushed a commit
that referenced
this pull request
Oct 24, 2026
ExitProcess terminates every other thread wherever it stands, then runs the exiting thread's FLS callbacks, thread_done_cb among them. A thread killed while holding HEAPS_LOCK (a pool worker in its own teardown) left heaps_unregister spinning forever, in a process TerminateProcess cannot end. ArtCraft hit it 2 times in ~1,060 runs; thread_done_cb+0x6e2 in their binary is the heaps_lock spin. thread_done_cb now returns when RtlDllShutdownInProgress() is true: nothing can adopt the heap and the address space is about to go. tests/exit_hang.rs parks a thread inside for_each_heap (HEAPS_LOCK held) and exits the process; it hung on 2.2.6 and exits in under a second now. Shipped alone as 2.2.7 from release/2.2.x (#56). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Windows, a multithreaded process could hang forever inside
ExitProcess, with one thread spinning at 100 % of a core inthread_done_cb.TerminateProcesscannot end it, because the process is already terminating. ArtCraft hit this 2 times in about 1,060 runs of DesignCraft's CLI (a rayon renderer, pinned to one core, on a loaded box), and never with the system allocator.Cause
ExitProcessterminates every other thread wherever it stands, then runs the exiting thread's FLS callbacks. One of them isthread_done_cb, which abandons the thread's heap.In the binary that hung,
thread_done_cb+0x6e2is thepause; lock cmpxchgbloop ofheaps_lock(), reached fromheaps_unregisterat the end ofthread_done_one. A pool worker was killed while it heldHEAPS_LOCKduring its own teardown, and nothing would ever release it.The same exposure applies to every other wait on that path:
abandoned_lock, the arena locks, and a page left inXFLAG_FREEING.Fix
thread_done_cb(Windows) returns at once whenRtlDllShutdownInProgress()is true. Nothing can adopt an abandoned heap at that point, and the address space is about to be discarded.raw-dylib, because windows-sys does not bind it.LdrShutdownProcess.Evidence
tests/exit_hang.rs: a child process parks a thread insideinit::for_each_heap(which holdsHEAPS_LOCK) and callsprocess::exitfrom a thread that owns a heap.TerminateThreadcould end it.cargo test -p rusty_alloc -p rusty_alloc-apipasses, includingabandon_rss,teardown_reclaimanddouble_free::abandoned_double_free_aborts, which need normal thread exits to still abandon their heaps. Clippy reports no warnings, andcargo fmt --checkis clean.x86_64-pc-windows-gnuand i686 Windows targets, so theirraw-dyliblink is untested.Not covered
Code that allocates on the exiting thread after the other threads are killed, such as a DLL detach routine, can still meet a lock a killed thread held. That is much rarer. Bounding or yielding those waits is separate work.
Release
The base is
release/2.2.x, a new branch at the 2.2.6 release commit (61ae839). Like 2.2.6, this release carries only the fix and not the unreleased speed work on main.8956990is the fix, the test and therusty_allocchangelog.b1211b1bumps the version to 2.2.7, the same shape as61ae839.rusty_alloc-v2.2.7,rusty_alloc-api-v2.2.7) and publishing are left to the maintainer.🤖 Generated with Claude Code