Skip to content

fix: avoid RequestManager deadlock in LSP rename by using doSyncRead - #3753

Open
mvanhorn wants to merge 2 commits into
eclipse-xtext:mainfrom
mvanhorn:fix/3735-xtext-lsp-rename-deadlock
Open

fix: avoid RequestManager deadlock in LSP rename by using doSyncRead#3753
mvanhorn wants to merge 2 commits into
eclipse-xtext:mainfrom
mvanhorn:fix/3735-xtext-lsp-rename-deadlock

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Jul 8, 2026

Copy link
Copy Markdown

Summary

LSP rename, prepareRename, and quickfix change conversion no longer queue a nested read request and block on it, which removes the RequestManager deadlock window: a nested read [R2] waits on a queued write [W], which awaits the in-flight outer read [R1], which waits on [R2]. The three call sites now use the existing ILanguageServerAccess#doSyncRead, which performs the same read without queueing.

Why this matters

#3735 traces both in-tree paths precisely: RenameService2.rename() and prepareRename() call doRead(uri, ...).get() from inside the read request that LanguageServerImpl already queued, and ChangeConverter2 does the same inside the code-action request. The reporter's company hits this regularly through slow quickfixes. The issue body itself proposes doSyncRead as the fix.

Changes

  • RenameService2.rename() restructures the .exceptionally(...).get() chain into a try/catch that preserves the existing contract: null on a root-cause FileNotFoundException when shouldPrepareRename is true, everything else rethrown via Exceptions.sneakyThrow.
  • ChangeConverter2 switches its doRead(...) + immediate join to doSyncRead. Every in-tree caller (the quickfix path through DiagnosticResolution and QuickFixCodeActionService) already runs inside a request and the method blocked on the future immediately, so the synchronous read is behavior-preserving apart from removing the deadlock window, answering the question the issue raised about whether the async form was ever required.

Testing

New regression test in Rename2Test exercises rename while requests interleave; the existing rename, prepareRename, and code-action tests in org.eclipse.xtext.ide.tests stay green.

Fixes #3735

@cdietrich
cdietrich requested a review from szarnekow July 9, 2026 06:59
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Test Results

  8 055 files  + 1    8 055 suites  +1   4h 6m 13s ⏱️ + 18m 0s
 43 168 tests + 1   42 577 ✅  -  6    584 💤 ±0  7 ❌ +7 
212 750 runs   - 18  209 825 ✅  - 22  2 918 💤  - 3  7 ❌ +7 

For more details on these failures, see this check.

Results for commit ed81a47. ± Comparison against base commit 66bc577.

♻️ This comment has been updated with latest results.

…jector

Embedding InterleavingRequestManager in Rename2Test overrode the
IRequestManager binding for every test in the class, breaking the
sibling rename tests. Move the harness and the deadlock regression
test into Rename2ConcurrencyTest so Rename2Test keeps the standard
RequestManager.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mvanhorn

Copy link
Copy Markdown
Author

Pushed ed81a47 fixing the test failures: the deadlock regression test bound InterleavingRequestManager inside Rename2Test, which overrode the IRequestManager binding for every test in that class and broke the sibling rename tests. The harness and the regression test now live in a dedicated Rename2ConcurrencyTest with its own injector, so Rename2Test runs against the standard RequestManager again. Also working on getting the ECA registered for my commit email.

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.

Deadlock potential in LSP RequestManager (and it's usages in rename & quickfix implementations)

1 participant