diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/NonBlockingReadActionImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/NonBlockingReadActionImpl.java index f096bd4bfa74..28845df42aa7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/NonBlockingReadActionImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/NonBlockingReadActionImpl.java @@ -4,6 +4,7 @@ package com.intellij.openapi.application.impl; import com.google.common.annotations.VisibleForTesting; import com.intellij.concurrency.SensitiveProgressWrapper; import com.intellij.diagnostic.ThreadDumper; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.*; import com.intellij.openapi.application.constraints.ExpirableConstrainedExecution; import com.intellij.openapi.application.constraints.Expiration; @@ -16,6 +17,7 @@ import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.progress.*; import com.intellij.openapi.progress.util.ProgressIndicatorUtils; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ex.ProjectEx; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; @@ -95,12 +97,12 @@ public class NonBlockingReadActionImpl @Override public NonBlockingReadAction inSmartMode(@NotNull Project project) { - return withConstraint(new InSmartMode(project), project); + return withConstraint(new InSmartMode(project), earlyDisposable(project)); } @Override public NonBlockingReadAction withDocumentsCommitted(@NotNull Project project) { - return withConstraint(new WithDocumentsCommitted(project, ModalityState.any()), project); + return withConstraint(new WithDocumentsCommitted(project, ModalityState.any()), earlyDisposable(project)); } @Override @@ -108,6 +110,17 @@ public class NonBlockingReadActionImpl return cancelIf(expireCondition); } + @NotNull + @Override + public NonBlockingReadActionImpl expireWith(@NotNull Disposable parentDisposable) { + return super.expireWith(earlyDisposable(parentDisposable)); + } + + private static Disposable earlyDisposable(Disposable disposable) { + // account for shared project in tests + return disposable instanceof ProjectEx ? ((ProjectEx)disposable).getEarlyDisposable() : disposable; + } + @Override public NonBlockingReadAction cancelWith(@NotNull ProgressIndicator progressIndicator) { LOG.assertTrue(myProgressIndicator == null, "Unspecified behaviour. Outer progress indicator is already set for the action."); @@ -319,6 +332,7 @@ public class NonBlockingReadActionImpl } private void reportCoalescingConflict(NonBlockingReadActionImpl.Submission current) { + ourTasks.remove(this); // the next line will throw in tests and leave this submission hanging forever LOG.error("Same coalesceBy arguments are already used by " + current.getComputationOrigin() + " so they can cancel each other. " + "Please make them more unique."); } @@ -507,16 +521,6 @@ public class NonBlockingReadActionImpl } } - @TestOnly - public static void cancelAllTasks() { - while (!ourTasks.isEmpty()) { - for (CancellablePromise task : ourTasks) { - task.cancel(); - } - WriteAction.run(() -> {}); // let background threads complete - } - } - @TestOnly public static void waitForAsyncTaskCompletion() { assert !ApplicationManager.getApplication().isWriteAccessAllowed(); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/ApplicationImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/ApplicationImplTest.java index 88bb1c5d61fb..a5cecbe441b0 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/ApplicationImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/ApplicationImplTest.java @@ -123,7 +123,7 @@ public class ApplicationImplTest extends LightPlatformTestCase { } private static void runReadWrites(final int readIterations, final int writeIterations, int expectedMs) { - NonBlockingReadActionImpl.cancelAllTasks(); // someone might've submitted a task depending on app events which we disable now + NonBlockingReadActionImpl.waitForAsyncTaskCompletion(); // someone might've submitted a task depending on app events which we disable now final ApplicationImpl application = (ApplicationImpl)ApplicationManager.getApplication(); Disposable disposable = Disposer.newDisposable(); application.disableEventsUntil(disposable); diff --git a/platform/testFramework/src/com/intellij/testFramework/HeavyPlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/HeavyPlatformTestCase.java index a927250d127a..bbe6cd91b8a5 100644 --- a/platform/testFramework/src/com/intellij/testFramework/HeavyPlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/HeavyPlatformTestCase.java @@ -459,6 +459,8 @@ public abstract class HeavyPlatformTestCase extends UsefulTestCase implements Da return; } + NonBlockingReadActionImpl.waitForAsyncTaskCompletion(); + UndoManagerImpl globalInstance = (UndoManagerImpl)UndoManager.getGlobalInstance(); if (globalInstance != null) { globalInstance.dropHistoryInTests(); @@ -477,8 +479,6 @@ public abstract class HeavyPlatformTestCase extends UsefulTestCase implements Da ((PsiManagerImpl)PsiManager.getInstance(defaultProject)).cleanupForNextTest(); } - NonBlockingReadActionImpl.cancelAllTasks(); - FileBasedIndex fileBasedIndex = app.getServiceIfCreated(FileBasedIndex.class); if (fileBasedIndex != null) { ((FileBasedIndexImpl)fileBasedIndex).cleanupForNextTest(); diff --git a/platform/testFramework/src/com/intellij/testFramework/TestApplicationManager.kt b/platform/testFramework/src/com/intellij/testFramework/TestApplicationManager.kt index fb7858a64387..73b25148b061 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestApplicationManager.kt +++ b/platform/testFramework/src/com/intellij/testFramework/TestApplicationManager.kt @@ -243,6 +243,7 @@ fun tearDownProjectAndApp(project: Project, appManager: TestApplicationManager? l.run { LegacyBridgeTestFrameworkUtils.dropCachesOnTeardown(project) } l.run { (ProjectManager.getInstance() as ProjectManagerImpl).forceCloseProject(project, !isLightProject) } + l.run { NonBlockingReadActionImpl.waitForAsyncTaskCompletion() } l.run { (appManager ?: TestApplicationManager.getInstanceIfCreated())?.setDataProvider(null) } l.run { UiInterceptors.clear() } @@ -346,6 +347,5 @@ fun waitForProjectLeakingThreads(project: Project, timeout: Long = 10, timeUnit: project.stopServicePreloading() } - NonBlockingReadActionImpl.cancelAllTasks() (project.serviceIfCreated() as GeneratedSourceFileChangeTrackerImpl?)?.cancelAllAndWait(timeout, timeUnit) } \ No newline at end of file