From 25d1b0acb69467983ac3422f75c7fa8d2b64e2e8 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 2 Jun 2017 19:47:44 +0200 Subject: [PATCH] commitAndRunReadAction from a progress should not hang forever (IDEA-173613) Signed-off-by: peter --- .../psi/impl/PsiDocumentManagerBase.java | 15 +++++---- .../psi/impl/PsiDocumentManagerImplTest.java | 31 +++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java index af3ba6d499a5..32bd49b594f4 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java @@ -506,8 +506,8 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen }); if (executed) break; - final Semaphore semaphore = new Semaphore(); - semaphore.down(); + TransactionId contextTransaction = TransactionGuard.getInstance().getContextTransaction(); + Semaphore semaphore = new Semaphore(1); application.invokeLater(() -> { if (myProject.isDisposed()) { // committedness doesn't matter anymore; give clients a chance to do checkCanceled @@ -515,7 +515,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen return; } - performWhenAllCommitted(() -> semaphore.up()); + performWhenAllCommitted(() -> semaphore.up(), contextTransaction); }, ModalityState.any()); semaphore.waitFor(); } @@ -528,6 +528,10 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen */ @Override public boolean performWhenAllCommitted(@NotNull final Runnable action) { + return performWhenAllCommitted(action, TransactionGuard.getInstance().getContextTransaction()); + } + + private boolean performWhenAllCommitted(@NotNull Runnable action, @Nullable TransactionId context) { ApplicationManager.getApplication().assertIsDispatchThread(); checkWeAreOutsideAfterCommitHandler(); @@ -543,13 +547,12 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen } actions.add(action); - TransactionId current = TransactionGuard.getInstance().getContextTransaction(); - if (current != ModalityState.NON_MODAL) { + if (context != null) { // re-add all uncommitted documents into the queue with this new modality // because this client obviously expects them to commit even inside modal dialog for (Document document : myUncommittedDocuments) { myDocumentCommitProcessor.commitAsynchronously(myProject, document, - "re-added with modality "+current+" because performWhenAllCommitted("+current+") was called", current); + "re-added with context "+context+" because performWhenAllCommitted("+context+") was called", context); } } return false; diff --git a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java index 2867f08a63b3..a13399b18772 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiDocumentManagerImplTest.java @@ -37,6 +37,10 @@ import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileTypes.PlainTextLanguage; import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.progress.util.ProgressWindow; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.ui.DialogWrapper; @@ -793,6 +797,33 @@ public class PsiDocumentManagerImplTest extends PlatformTestCase { assertEquals(PlainTextLanguage.INSTANCE, file2.getLanguage()); } + public void testAsyncCommitHappensInProgressStartedFromTransaction() throws IOException { + PsiFile file = getPsiManager().findFile(getVirtualFile(createTempFile("X.txt", ""))); + Document document = file.getViewProvider().getDocument(); + + Semaphore semaphore = new Semaphore(1); + TransactionGuard.submitTransaction(getTestRootDisposable(), () -> { + WriteCommandAction.runWriteCommandAction(myProject, () -> { + document.insertString(0, "x"); + + ProgressManager.getInstance().runProcessWithProgressAsynchronously(new Task.Backgroundable(myProject, "Title", false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + getPsiDocumentManager().commitAndRunReadAction(() -> semaphore.up()); + } + }, new ProgressWindow(false, myProject)); + }); + }); + int iteration = 0; + while (!semaphore.waitFor(10)) { + UIUtil.dispatchAllInvocationEvents(); + if (++iteration > 3000) { + printThreadDump(); + fail("Couldn't wait for commit"); + } + } + } + private static void assertLargeFileContentLimited(@NotNull String content, @NotNull VirtualFile vFile, @NotNull Document document) { Charset charset = EncodingManager.getInstance().getEncoding(vFile, false); float bytesPerChar = charset == null ? 2 : charset.newEncoder().averageBytesPerChar();