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 1288210c477d..901470c06db4 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 @@ -423,39 +423,44 @@ public final class NonBlockingReadActionImpl implements NonBlockingReadAction } T executeSynchronously() { - while (true) { - attemptComputation(); + try { + while (true) { + attemptComputation(); - if (isCancelled()) { - throw new ProcessCanceledException(); - } - if (isDone()) { - try { - return blockingGet(0, TimeUnit.MILLISECONDS); - } - catch (TimeoutException e) { - throw new RuntimeException(e); - } - } - - ProgressIndicatorUtils.checkCancelledEvenWithPCEDisabled(myProgressIndicator); - ContextConstraint[] constraints = builder.myConstraints; - if (shouldFinishOnEdt() || constraints.length != 0) { - Semaphore semaphore = new Semaphore(1); - invokeLater(() -> { - if (checkObsolete()) { - semaphore.up(); - } - else { - BaseConstrainedExecution.scheduleWithinConstraints(semaphore::up, null, constraints); - } - }); - ProgressIndicatorUtils.awaitWithCheckCanceled(semaphore, myProgressIndicator); if (isCancelled()) { throw new ProcessCanceledException(); } + if (isDone()) { + try { + return blockingGet(0, TimeUnit.MILLISECONDS); + } + catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + + ProgressIndicatorUtils.checkCancelledEvenWithPCEDisabled(myProgressIndicator); + ContextConstraint[] constraints = builder.myConstraints; + if (shouldFinishOnEdt() || constraints.length != 0) { + Semaphore semaphore = new Semaphore(1); + invokeLater(() -> { + if (checkObsolete()) { + semaphore.up(); + } + else { + BaseConstrainedExecution.scheduleWithinConstraints(semaphore::up, null, constraints); + } + }); + ProgressIndicatorUtils.awaitWithCheckCanceled(semaphore, myProgressIndicator); + if (isCancelled()) { + throw new ProcessCanceledException(); + } + } } } + finally { + cleanupIfNeeded(); + } } private boolean attemptComputation() { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/NonBlockingReadActionTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/NonBlockingReadActionTest.java index 3255c1a7193b..7bce1736161d 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/NonBlockingReadActionTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/NonBlockingReadActionTest.java @@ -256,6 +256,32 @@ public class NonBlockingReadActionTest extends LightPlatformTestCase { } } + public void testDoNotLeakDisposablesOnCancelledIndicator() { + ProgressIndicator outerIndicator = new EmptyProgressIndicator(); + Disposable disposable = Disposer.newDisposable(); + try { + Future future = ApplicationManager.getApplication().executeOnPooledThread(() -> { + assertThrows(ProcessCanceledException.class, () -> { + ReadAction.nonBlocking(() -> { + outerIndicator.cancel(); + throw new ProcessCanceledException(); + }) + .expireWith(disposable) + .wrapProgress(outerIndicator) + .executeSynchronously(); + }); + }); + waitForFuture(future); + + Disposer.disposeChildren(disposable, (child) -> { + throw new IllegalStateException(child.toString()); + }); + } + finally { + Disposer.dispose(disposable); + } + } + public void testSyncExecutionHonorsConstraints() { setupUncommittedDocument();