镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Keep a shared thread pool alive when the thread count is reset - #24770

Open
shoumikhin wants to merge 1 commit into
pytorch:mainfrom
shoumikhin:threadpool-keep-shared-pools
Open

shoumikhin wants to merge 1 commit into
pytorch:mainfrom
shoumikhin:threadpool-keep-shared-pools

Conversation

@shoumikhin

Copy link
Copy Markdown
Contributor

What is wrong today

From Python, _unsafe_reset_threadpool(n) is the only way to set the CPU thread count. If it runs after a method that uses XNNPACK is loaded, the next runs can crash or hang:

method = Runtime.get().load_program(pte.buffer).load_method("forward")
_unsafe_reset_threadpool(16)
for _ in range(20):
    method.execute([x])  # segfault, abort, or hang

On Linux x86_64 with an 8-thread default pool, a reset to 16 after loading crashed in 10 of 12 runs, and a reset to 4 also in 10 of 12. The Android bindings reset the pool each time a Module is created, so a second Module can hit it too.

Why it happens

The reset destroys the current pthreadpool and creates a new one. But XNNPACK got that pthreadpool from get_pthreadpool() when the method was loaded, and it keeps using it. So the loaded method runs on a freed pool.

Resizing the pool in place is not safe either. XNNPACK sizes its per-thread buffers by the thread count when it plans a method, and it does not plan again when the count changes.

What this change does

A reset no longer destroys a pthreadpool that get_pthreadpool() has handed out. It stays alive with its size, next to the new one:

  • XNNPACK methods loaded before the reset keep running on their old pool.
  • Methods loaded after the reset, and ExecuTorch's own kernels, use the new pool.
  • A reset back to a kept size reuses that pool, so at most one pool per size is kept. _unsafe_destroy_threadpool() frees kept pools too.
  • A pool that was never handed out is destroyed as before, so the usual "reset, then load" order costs nothing extra.

The header and the Python docstring now explain this.

Tests

  • New ThreadPoolTest.ResetKeepsHandedOutPthreadpoolUsable: after a reset, the held pool still runs work on 2 threads, and a reset back to its size reuses it. It fails without the fix and passes with it.
  • Ran extension_threadpool_test, the native CPU engine tests, the Python tests for the pybindings and XNNPACK, and the LLM custom op tests that call the reset, on Linux x86_64. Tests that failed there fail on main too.
  • The script above now passes after resets to 16 and to 4. With 20 resets between 2 thread counts and a new method loaded after each, all 21 methods stay correct and the process gains no threads from round to round.

Fixes #24765

`_unsafe_reset_threadpool()` destroyed the current pthreadpool and created
a new one. Delegates such as XNNPACK get that pthreadpool from
`get_pthreadpool()` when a method is loaded and keep using it, so after a
reset the loaded method ran on a freed pool and crashed or hung. From
Python the reset is the only way to set the thread count, so calling it
after `load_method` was enough to hit this.

Now a pthreadpool that `get_pthreadpool()` has handed out stays alive
with its size when a reset replaces it. XNNPACK methods loaded before the
reset keep running on it, and methods loaded after it use the new pool. A
reset back to a kept size reuses that pool, so at most one pool per size
is kept, and `_unsafe_destroy_threadpool()` frees the kept pools too. A
pool that was never handed out is destroyed as before, so a reset before
loading costs nothing extra. Resizing a pool in place is not an option,
because XNNPACK sizes per-thread buffers by the thread count when it
plans a method.

Fixes pytorch#24765
@shoumikhin shoumikhin added the release notes: runtime Changes related to the core runtime which loads the program methods, initializes delegates, and runs label Nov 7, 2026
@shoumikhin shoumikhin added the release notes: runtime Changes related to the core runtime which loads the program methods, initializes delegates, and runs label Nov 7, 2026
Copilot AI balanced review requested due to automatic review settings November 7, 2026 05:16
@pytorch-bot

pytorch-bot Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24770

Note: Links to docs will display an error until the docs builds have been completed.

⏳ No Failures, 42 Pending

As of commit ce80ef8 with merge base 75af78c (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 7, 2026

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@shoumikhin

Copy link
Copy Markdown
Contributor Author

@claude /pr-review

@claude

claude Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

PR Review in progress

  • Gather context (diff, changed files, checklist)
  • Run three independent reviewers
  • Consolidate findings
  • Fact-check findings
  • Post review

View job run

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. release notes: runtime Changes related to the core runtime which loads the program methods, initializes delegates, and runs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No safe way to set the CPU thread count from Python: _unsafe_reset_threadpool after load_method crashes or hangs XNNPACK

2 participants