From d2f4eeec33a5473521e5587087695cb3504f9334 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 12 May 2016 20:36:44 +0300 Subject: [PATCH] vcs: sanitize BackgroundTaskQueue --- .../progress/impl/CoreProgressManager.java | 12 +- .../openapi/progress/RunBackgroundable.java | 35 +--- .../openapi/progress/BackgroundTaskQueue.java | 165 +++++++----------- .../progress/BackgroundTasksMonitor.java | 92 ---------- .../progress/impl/ProgressManagerImpl.java | 2 +- .../com/intellij/vcs/log/data/VcsLogData.java | 2 +- 6 files changed, 71 insertions(+), 237 deletions(-) delete mode 100644 platform/platform-impl/src/com/intellij/openapi/progress/BackgroundTasksMonitor.java diff --git a/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java b/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java index b8c85d28034e..ea0ee7362050 100644 --- a/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java +++ b/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java @@ -325,16 +325,16 @@ public class CoreProgressManager extends ProgressManager implements Disposable { @NotNull public Future runProcessWithProgressAsynchronously(@NotNull final Task.Backgroundable task, - @NotNull final ProgressIndicator progressIndicator, - @Nullable final Runnable continuation) { + @NotNull final ProgressIndicator progressIndicator, + @Nullable final Runnable continuation) { return runProcessWithProgressAsynchronously(task, progressIndicator, continuation, ModalityState.NON_MODAL); } @NotNull public Future runProcessWithProgressAsynchronously(@NotNull final Task.Backgroundable task, - @NotNull final ProgressIndicator progressIndicator, - @Nullable final Runnable continuation, - @NotNull final ModalityState modalityState) { + @NotNull final ProgressIndicator progressIndicator, + @Nullable final Runnable continuation, + @NotNull final ModalityState modalityState) { if (progressIndicator instanceof Disposable) { Disposer.register(ApplicationManager.getApplication(), (Disposable)progressIndicator); } @@ -371,7 +371,7 @@ public class CoreProgressManager extends ProgressManager implements Disposable { return ApplicationManager.getApplication().executeOnPooledThread(action); } - protected boolean runProcessWithProgressSynchronously(@NotNull final Task task, @Nullable final JComponent parentComponent) { + public boolean runProcessWithProgressSynchronously(@NotNull final Task task, @Nullable final JComponent parentComponent) { final Ref exceptionRef = new Ref(); TaskContainer taskContainer = new TaskContainer(task) { @Override diff --git a/platform/platform-api/src/com/intellij/openapi/progress/RunBackgroundable.java b/platform/platform-api/src/com/intellij/openapi/progress/RunBackgroundable.java index 6c38ccdbcffc..eb4a22fdcb15 100644 --- a/platform/platform-api/src/com/intellij/openapi/progress/RunBackgroundable.java +++ b/platform/platform-api/src/com/intellij/openapi/progress/RunBackgroundable.java @@ -15,45 +15,14 @@ */ package com.intellij.openapi.progress; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class RunBackgroundable { private RunBackgroundable() { } + @Deprecated public static void run(@NotNull final Task task) { - final ProgressManager pm = ProgressManager.getInstance(); - if (ApplicationManager.getApplication().isDispatchThread()) { - pm.run(task); - } else { - runIfBackgroundThread(task, pm.getProgressIndicator(), null); - } - } - - public static void runIfBackgroundThread(final Task task, final ProgressIndicator pi, @Nullable final Runnable pooledContinuation) { - boolean canceled = true; - try { - task.run(pi); - canceled = pi != null && pi.isCanceled(); - } catch (ProcessCanceledException e) { - // - } finally { - if (pooledContinuation != null) { - pooledContinuation.run(); - } - } - - final boolean finalCanceled = canceled; - UIUtil.invokeLaterIfNeeded(() -> { - if (finalCanceled) { - task.onCancel(); - } - else { - task.onSuccess(); - } - }); + ProgressManager.getInstance().run(task); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/BackgroundTaskQueue.java b/platform/platform-impl/src/com/intellij/openapi/progress/BackgroundTaskQueue.java index 3436410db2e1..b1a18fad2391 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/BackgroundTaskQueue.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/BackgroundTaskQueue.java @@ -19,17 +19,12 @@ package com.intellij.openapi.progress; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator; import com.intellij.openapi.progress.impl.ProgressManagerImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Getter; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.PairConsumer; -import com.intellij.util.PlusMinus; import com.intellij.util.concurrency.QueueProcessor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,42 +34,44 @@ import org.jetbrains.annotations.TestOnly; * Runs backgroundable tasks one by one. * To add a task to the queue use {@link #run(com.intellij.openapi.progress.Task.Backgroundable)} * BackgroundTaskQueue may have a title - this title will be used if the task which is currently running doesn't have a title. - * - * @author yole - * @author Kirill Likhodedov */ @SomeQueue public class BackgroundTaskQueue { - private final static String ourMonitorFlag = "monitor.background.queue.load"; - private static final Logger LOG = Logger.getInstance(BackgroundTaskQueue.class.getName()); - //private final Project myProject; - private final QueueProcessor>> myProcessor; - private Boolean myForcedTestMode; - private final PlusMinus myMonitor; + @NotNull private final String myTitle; + @NotNull private final QueueProcessor myProcessor; + + private boolean myForceAsyncInTests = false; public BackgroundTaskQueue(@Nullable Project project, @NotNull String title) { - this(project, title, null); - } + myTitle = title; - public BackgroundTaskQueue(@Nullable final Project project, @NotNull String title, final Boolean forcedHeadlessMode) { - myMonitor = Boolean.TRUE.equals(Boolean.getBoolean(ourMonitorFlag)) ? new BackgroundTasksMonitor(title) : new PlusMinus.Empty(); - final boolean headless = forcedHeadlessMode != null ? forcedHeadlessMode : ApplicationManager.getApplication().isHeadlessEnvironment(); + Condition disposeCondition = project != null ? project.getDisposed() : ApplicationManager.getApplication().getDisposed(); - final QueueProcessor.ThreadToUse threadToUse = headless ? QueueProcessor.ThreadToUse.POOLED : QueueProcessor.ThreadToUse.AWT; - final PairConsumer>, Runnable> consumer - = headless ? new BackgroundableHeadlessRunner() : new BackgroundableUnderProgressRunner(title, project, myMonitor); + myProcessor = new QueueProcessor((data, continuation) -> { + Task.Backgroundable task = data.task; + ProgressIndicator indicator = data.indicator; + ModalityState modalityState = data.modalityState; - myProcessor = new QueueProcessor>>(consumer, true, - threadToUse, new Condition() { - @Override public boolean value(Object o) { - if (project == null) return ApplicationManager.getApplication().isDisposed(); - if (project.isDefault()) { - return project.isDisposed(); - } else { - return !ApplicationManager.getApplication().isUnitTestMode() && !project.isOpen() || project.isDisposed(); - } + if (StringUtil.isEmptyOrSpaces(task.getTitle())) { + task.setTitle(myTitle); + } + + boolean synchronous = (task.isHeadless() && !myForceAsyncInTests) || + (task.isConditionalModal() && !task.shouldStartInBackground()); + + ProgressManagerImpl pm = (ProgressManagerImpl)ProgressManager.getInstance(); + if (synchronous) { + try { + pm.runProcessWithProgressSynchronously(task, null); } - }); + finally { + continuation.run(); + } + } + else { + pm.runProcessWithProgressAsynchronously(task, indicator, continuation, modalityState); + } + }, true, QueueProcessor.ThreadToUse.AWT, disposeCondition); } public void clear() { @@ -84,91 +81,51 @@ public class BackgroundTaskQueue { public boolean isEmpty() { return myProcessor.isEmpty(); } - + public void waitForTasksToFinish() { myProcessor.waitFor(); } - public void run(Task.Backgroundable task) { + public void run(@NotNull Task.Backgroundable task) { run(task, null, null); } - public void run(Task.Backgroundable task, final ModalityState state, final Getter pi) { - myMonitor.plus(task.getTitle()); - if (isTestMode()) { // test tasks are executed in this thread without the progress manager - RunBackgroundable.runIfBackgroundThread(task, new EmptyProgressIndicator(), null); - } else { - myProcessor.add(Pair.create(task, pi), state); - } - } - - private static class BackgroundableHeadlessRunner implements PairConsumer>, Runnable> { - @Override - public void consume(Pair> pair, Runnable runnable) { - final Task.Backgroundable backgroundable = pair.getFirst(); - // synchronously - ProgressManager.getInstance().run(backgroundable); - runnable.run(); - } - } - - private static class BackgroundableUnderProgressRunner implements PairConsumer>, Runnable> { - private final String myTitle; - private final Project myProject; - private final PlusMinus myMonitor; - - public BackgroundableUnderProgressRunner(String title, final Project project, PlusMinus monitor) { - myTitle = title; - myProject = project; - myMonitor = monitor; - } - - @Override - public void consume(final Pair> pair, final Runnable runnable) { - myMonitor.minus(pair.getFirst().getTitle()); - final Task.Backgroundable backgroundable = pair.getFirst(); - final ProgressIndicator[] pi = new ProgressIndicator[1]; - final boolean taskTitleIsEmpty = StringUtil.isEmptyOrSpaces(backgroundable.getTitle()); - - final Runnable wrappedTask = () -> { - // calls task's run and onCancel() or onSuccess(); call continuation after task.run() - RunBackgroundable.runIfBackgroundThread(backgroundable, - pi[0] == null ? ProgressManager.getInstance().getProgressIndicator() : pi[0], runnable); - }; - - final ProgressManager pm = ProgressManager.getInstance(); - if (backgroundable.isConditionalModal() && ! backgroundable.shouldStartInBackground()) { - pm.runProcessWithProgressSynchronously(wrappedTask, taskTitleIsEmpty ? myTitle : backgroundable.getTitle(), - backgroundable.isCancellable(), myProject); - } else { - if (pair.getSecond() != null) { - pi[0] = pair.getSecond().get(); - } - if (pi[0] == null) { - if (taskTitleIsEmpty) { - backgroundable.setTitle(myTitle); - } - pi[0] = new BackgroundableProcessIndicator(backgroundable); - } - - ((ProgressManagerImpl)ProgressManager.getInstance()).runProcessWithProgressAsynchronously(backgroundable, pi[0], runnable); + public void run(@NotNull Task.Backgroundable task, @Nullable ModalityState modalityState, @Nullable ProgressIndicator indicator) { + if (modalityState == null) modalityState = ModalityState.NON_MODAL; + if (indicator == null) { + if (ApplicationManager.getApplication().isHeadlessEnvironment()) { + indicator = new EmptyProgressIndicator(); + } + else { + indicator = new BackgroundableProcessIndicator(task); } } + myProcessor.add(new TaskData(task, modalityState, indicator), modalityState); } - public boolean isTestMode() { - if (myForcedTestMode != null) return myForcedTestMode; - return ApplicationManager.getApplication().isUnitTestMode(); + private static class TaskData { + @NotNull public final Task.Backgroundable task; + @NotNull public final ModalityState modalityState; + @NotNull public final ProgressIndicator indicator; + + public TaskData(@NotNull Task.Backgroundable task, @NotNull ModalityState modalityState, @NotNull ProgressIndicator indicator) { + this.task = task; + this.modalityState = modalityState; + this.indicator = indicator; + } } @TestOnly - public void setForcedTestMode(Boolean forcedTestMode, Disposable parentDisposable) { - myForcedTestMode = forcedTestMode; - Disposer.register(parentDisposable, new Disposable() { - @Override - public void dispose() { - myForcedTestMode = null; - } - }); + public void setForceAsyncInTests(boolean value, @Nullable Disposable disposable) { + ApplicationManager.getApplication().assertIsDispatchThread(); + myForceAsyncInTests = value; + if (disposable != null) { + Disposer.register(disposable, new Disposable() { + @Override + public void dispose() { + myForceAsyncInTests = false; + } + }); + } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/BackgroundTasksMonitor.java b/platform/platform-impl/src/com/intellij/openapi/progress/BackgroundTasksMonitor.java deleted file mode 100644 index ab1040eb3f7e..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/progress/BackgroundTasksMonitor.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2000-2011 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.progress; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.PlusMinus; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author irengrig - * Date: 4/13/11 - * Time: 5:32 PM - */ -public class BackgroundTasksMonitor implements PlusMinus { - private final static Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.BackgroundTasksMonitor"); - private static final long ourStatInterval = 300000; - private long myRecentTime; - private final Map myMap; - private final Map myMaxMap; - private final Object myLock; - private final String myQueueTitle; - - public BackgroundTasksMonitor(final String queueTitle) { - myQueueTitle = queueTitle; - myMap = new HashMap(); - myMaxMap = new HashMap(); - myLock = new Object(); - myRecentTime = 0; - } - - @Override - public void plus(String title) { - synchronized (myLock) { - final Integer previous = myMap.get(title); - final int newVal = previous == null ? 1 : (previous + 1); - myMap.put(title, newVal); - final Integer max = myMaxMap.get(title); - if (max == null || max < newVal) { - myMaxMap.put(title, newVal); - } - reportStatistics(); - } - } - - - @Override - public void minus(String title) { - synchronized (myLock) { - final Integer integer = myMap.get(title); - assert integer != null; - if (integer == 1) { - myMap.remove(title); - } else { - myMap.put(title, integer - 1); - } - reportStatistics(); - } - } - - private void reportStatistics() { - final long time = System.currentTimeMillis(); - if (time - ourStatInterval < myRecentTime) return; - final StringBuilder sb = new StringBuilder("BackgroundTaskQueue '" + myQueueTitle + "' usage statistics\n"); - sb.append("----------------------------------------------------\n"); - sb.append("Current Values:"); - for (Map.Entry entry : myMap.entrySet()) { - sb.append(entry.getKey()).append(": ").append(entry.getValue()); - } - sb.append("\nMaximum Values:"); - for (Map.Entry entry : myMaxMap.entrySet()) { - sb.append('\n').append(entry.getKey()).append(": ").append(entry.getValue()); - } - sb.append("----------------------------------------------------\n"); - LOG.info(sb.toString()); - myRecentTime = time; - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java index 485327594487..c32600945cee 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java @@ -71,7 +71,7 @@ public class ProgressManagerImpl extends CoreProgressManager implements Disposab } @Override - protected boolean runProcessWithProgressSynchronously(@NotNull final Task task, @Nullable final JComponent parentComponent) { + public boolean runProcessWithProgressSynchronously(@NotNull final Task task, @Nullable final JComponent parentComponent) { final long start = System.currentTimeMillis(); final boolean result = super.runProcessWithProgressSynchronously(task, parentComponent); if (result) { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogData.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogData.java index 38e61dbab592..353967347a3f 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogData.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogData.java @@ -248,7 +248,7 @@ public class VcsLogData implements Disposable, VcsLogDataProvider { } } }; - myDataLoaderQueue.run(backgroundable, null, () -> myRefresher.getProgress().createProgressIndicator(backgroundable)); + myDataLoaderQueue.run(backgroundable, null, myRefresher.getProgress().createProgressIndicator(backgroundable)); } /**