From 4f5a775b1b8ac0074ef656c85b97f5b9a8b44e19 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 3 Nov 2015 14:13:14 +0300 Subject: [PATCH] pooled thread leak after canceling inspection unexpectedly --- .../ex/GlobalInspectionContextImpl.java | 74 +++++++++++-------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index b4530caaddcd..3d6fc6488e88 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -84,10 +84,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Constructor; import java.util.*; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.*; public class GlobalInspectionContextImpl extends GlobalInspectionContextBase implements GlobalInspectionContext { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.GlobalInspectionContextImpl"); @@ -389,7 +386,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp final BlockingQueue filesToInspect = new ArrayBlockingQueue(1000); final Queue filesFailedToInspect = new LinkedBlockingQueue(); // use original progress indicator here since we don't want it to cancel on write action start - startIterateScope(scope, localScopeFiles, headlessEnvironment, filesToInspect, progressIndicator); + Future future = startIterateScopeInBackground(scope, localScopeFiles, headlessEnvironment, filesToInspect, progressIndicator); Processor processor = new Processor() { @Override @@ -410,31 +407,43 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp return true; } }; - while (true) { - Disposable disposable = Disposer.newDisposable(); - ProgressIndicator wrapper = new SensitiveProgressWrapper(progressIndicator); - wrapper.start(); - ProgressIndicatorUtils.forceWriteActionPriority(wrapper, disposable); + try { + while (true) { + Disposable disposable = Disposer.newDisposable(); + ProgressIndicator wrapper = new SensitiveProgressWrapper(progressIndicator); + wrapper.start(); + ProgressIndicatorUtils.forceWriteActionPriority(wrapper, disposable); - try { - // use wrapper here to cancel early when write action start but do not affect the original indicator - ((JobLauncherImpl)JobLauncher.getInstance()).processQueue(filesToInspect, filesFailedToInspect, wrapper, TOMBSTONE, processor); - break; - } - catch (ProcessCanceledException ignored) { - progressIndicator.checkCanceled(); - // PCE may be thrown from inside wrapper when write action started - // go on with the write and then resume processing the rest of the queue - assert !ApplicationManager.getApplication().isReadAccessAllowed(); - assert !ApplicationManager.getApplication().isDispatchThread(); + try { + // use wrapper here to cancel early when write action start but do not affect the original indicator + ((JobLauncherImpl)JobLauncher.getInstance()).processQueue(filesToInspect, filesFailedToInspect, wrapper, TOMBSTONE, processor); + break; + } + catch (ProcessCanceledException ignored) { + progressIndicator.checkCanceled(); + // PCE may be thrown from inside wrapper when write action started + // go on with the write and then resume processing the rest of the queue + assert !ApplicationManager.getApplication().isReadAccessAllowed(); + assert !ApplicationManager.getApplication().isDispatchThread(); - // wait for write action to complete - ApplicationManager.getApplication().runReadAction(EmptyRunnable.getInstance()); - } - finally { - Disposer.dispose(disposable); + // wait for write action to complete + ApplicationManager.getApplication().runReadAction(EmptyRunnable.getInstance()); + } + finally { + Disposer.dispose(disposable); + } } } + finally { + filesToInspect.clear(); // let background thread a chance to put TOMBSTONE and complete + try { + future.get(30, TimeUnit.SECONDS); + } + catch (Exception e) { + LOG.error(e); + } + } + progressIndicator.checkCanceled(); for (Tools tools : globalSimpleTools) { @@ -500,12 +509,13 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp private static final PsiFile TOMBSTONE = PsiUtilCore.NULL_PSI_FILE; - private void startIterateScope(@NotNull final AnalysisScope scope, - @Nullable final Collection localScopeFiles, - final boolean headlessEnvironment, - @NotNull final BlockingQueue outFilesToInspect, - @NotNull final ProgressIndicator progressIndicator) { - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @NotNull + private Future startIterateScopeInBackground(@NotNull final AnalysisScope scope, + @Nullable final Collection localScopeFiles, + final boolean headlessEnvironment, + @NotNull final BlockingQueue outFilesToInspect, + @NotNull final ProgressIndicator progressIndicator) { + return ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @Override public void run() { try {