Skip to content

Fix deadlock in Extract Method when multiple scopes exist#713

Open
nafg wants to merge 1 commit intoJetBrains:idea253.xfrom
nafg:fix/extract-method-deadlock
Open

Fix deadlock in Extract Method when multiple scopes exist#713
nafg wants to merge 1 commit intoJetBrains:idea253.xfrom
nafg:fix/extract-method-deadlock

Conversation

@nafg
Copy link

@nafg nafg commented Feb 10, 2026

Note: This PR was authored by Claude Code (Anthropic's CLI tool).

YouTrack: SCL-25001

Summary

  • Replace PsiTargetNavigator with JBPopupFactory in showPsiChooser to fix a deadlock that can freeze IntelliJ when "Extract Method" is invoked on code with multiple extraction scopes while another operation holds the indexing lock

Problem

PsiTargetNavigator.createPopup() internally calls runBlocking on the EDT via computeItems()Utils.computeWithProgressIcon(). The dispatched coroutine workers then contend for the indexing lock (ChangeTrackingValueContainer), which can't be released because the EDT is blocked. This creates a permanent deadlock with 34 threads blocked.

This is a race condition — it requires both multiple extraction scopes (triggering PsiTargetNavigator) AND concurrent index lock contention (e.g., Find Occurrences running at the same time). It does not freeze every time, but runBlocking on the EDT is still architecturally wrong.

See SCL-25001 for full thread dump analysis.

Fix

showPsiChooser now delegates to showChooserGeneric, which uses JBPopupFactory — a safe popup mechanism that doesn't use runBlocking. This method already exists in the same file and provides the same functionality (title, presentation, highlighting, selection callback).

The items passed to showPsiChooser are already fully computed PsiElements with pre-computed presentation strings, so there is no need for the async computation that PsiTargetNavigator provides.

Test plan

  • Existing Extract Method tests pass (ScalaExtractMethodTestBase, ScalaExtractMethodScopeTest, etc.)
  • Manual test: invoke Extract Method on Scala code with multiple valid extraction scopes while Find Occurrences or indexing is active — the scope chooser popup should appear without freezing

@unkarjedy
Copy link
Member

@nafg Thanks for the PR.

Note: This PR was authored by Claude Code (Anthropic's CLI tool).

And thanks for the note.
@nafg Have you manually verified that the fix works and that there are no "obvious" regressions in the behavior?

…st #SCL-25001 fixed

Replace PsiTargetNavigator with JBPopupFactory in showPsiChooser
to avoid a deadlock caused by runBlocking on the EDT.

PsiTargetNavigator.createPopup() internally calls computeItems()
which uses runBlocking, blocking the EDT while dispatching work to
coroutine workers. Those workers contend for the indexing lock
(ChangeTrackingValueContainer), which can't be released because the
EDT is blocked. This causes a permanent deadlock (34 threads blocked).

The fix delegates to showChooserGeneric which uses JBPopupFactory —
a safe popup mechanism already used elsewhere in the same file.

Also related: JetBrains#712

Co-Authored-By: Claude Code (Anthropic) <noreply@anthropic.com>
@nafg nafg force-pushed the fix/extract-method-deadlock branch from ebccf84 to 985eb4a Compare February 11, 2026 03:06
@nafg
Copy link
Author

nafg commented Feb 11, 2026

[Comment authored by Claude Code on behalf of @nafg]

@unkarjedy Thanks for the quick response!

Clarification: The deadlock is a race condition, not a guaranteed freeze. It requires two conditions simultaneously:

  1. Multiple extraction scopes exist (so showPsiChooserPsiTargetNavigator.createPopup()runBlocking is called on the EDT)
  2. Something else is holding the indexing lock at the same time (in my case, it was Find Occurrences running concurrently)

Without index contention, the runBlocking workers complete quickly and there's no visible issue. But runBlocking on the EDT is still architecturally wrong — it blocks the UI thread and will deadlock whenever there's any index lock contention.

Re: manual verification — I haven't tested the built plugin yet. I'm planning to upgrade to the latest EAP, reproduce the deadlock, then verify the fix. The change itself is minimal: showPsiChooser now delegates to showChooserGeneric, which already exists in the same file and is already used by other callers (e.g., invokeOnSelected at line 879 for expression/type element choosers). Both methods go through the same showChooserImpl for highlighting and callbacks — the only difference is JBPopupFactory vs PsiTargetNavigator.

I'll follow up once I've verified.

@nafg
Copy link
Author

nafg commented Feb 11, 2026

[Comment authored by Claude Code on behalf of @nafg]

Reproduction attempt on 2026.1 EAP:

I upgraded to IntelliJ IDEA 2026.1 EAP and attempted to reproduce the deadlock. I was unable to trigger it — even while running Find Usages on Any (a heavily-referenced symbol), the Extract Method scope chooser dialog appeared without freezing.

This is consistent with it being a race condition: the original deadlock (captured in the thread dump from 2025.3) required the PsiTargetNavigator.computeItems() coroutine workers to contend for the indexing lock at exactly the right moment. On 2026.1, Find Usages appears to be faster, which narrows the race window.

That said, the underlying issue — runBlocking on the EDT inside PsiTargetNavigator.createPopup() — is still present in the code on both idea253.x and idea261.x. The thread dump from 2025.3 demonstrates that this can and does deadlock under contention, even if it's hard to trigger on demand.

@nafg
Copy link
Author

nafg commented Feb 11, 2026

Make what you like of Claude's opinions...

Find Occurrences seems WAY faster and smoother btw

@unkarjedy
Copy link
Member

@nafg Thanks for checking it!
I realise that testing deadlocks & race conditions can be tricky.
In my question:

Have you manually verified that the fix works and that there are no "obvious" regressions in the behavior?

I primarily meant to check that there is no regressions in the feature behavior and it works.

I am asking just in case, because in the past we had some AI-generated trash PR that had ton's of mistakes and it turned out it was reported by an agent and the user never even tried to verify it.

Thanks, we will look into it

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.

2 participants