fix: avoid RequestManager deadlock in LSP rename by using doSyncRead - #3753
fix: avoid RequestManager deadlock in LSP rename by using doSyncRead#3753mvanhorn wants to merge 2 commits into
Conversation
Test Results 8 055 files + 1 8 055 suites +1 4h 6m 13s ⏱️ + 18m 0s 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>
|
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. |
Summary
LSP rename, prepareRename, and quickfix change conversion no longer queue a nested read request and block on it, which removes the
RequestManagerdeadlock 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 existingILanguageServerAccess#doSyncRead, which performs the same read without queueing.Why this matters
#3735 traces both in-tree paths precisely:
RenameService2.rename()andprepareRename()calldoRead(uri, ...).get()from inside the read request thatLanguageServerImplalready queued, andChangeConverter2does the same inside the code-action request. The reporter's company hits this regularly through slow quickfixes. The issue body itself proposesdoSyncReadas the fix.Changes
RenameService2.rename()restructures the.exceptionally(...).get()chain into a try/catch that preserves the existing contract: null on a root-causeFileNotFoundExceptionwhenshouldPrepareRenameis true, everything else rethrown viaExceptions.sneakyThrow.ChangeConverter2switches itsdoRead(...)+ immediate join todoSyncRead. Every in-tree caller (the quickfix path throughDiagnosticResolutionandQuickFixCodeActionService) 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
Rename2Testexercises rename while requests interleave; the existing rename, prepareRename, and code-action tests inorg.eclipse.xtext.ide.testsstay green.Fixes #3735