vcs: sanitize BackgroundTaskQueue

This commit is contained in:
Aleksey Pivovarov
2016-05-24 17:47:40 +03:00
committed by Aleksey Pivovarov
parent 5c4046a522
commit d2f4eeec33
6 changed files with 71 additions and 237 deletions
@@ -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<Exception> exceptionRef = new Ref<Exception>();
TaskContainer taskContainer = new TaskContainer(task) {
@Override
@@ -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);
}
}
@@ -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<Pair<Task.Backgroundable, Getter<ProgressIndicator>>> myProcessor;
private Boolean myForcedTestMode;
private final PlusMinus<String> myMonitor;
@NotNull private final String myTitle;
@NotNull private final QueueProcessor<TaskData> 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<String>();
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<Pair<Task.Backgroundable, Getter<ProgressIndicator>>, Runnable> consumer
= headless ? new BackgroundableHeadlessRunner() : new BackgroundableUnderProgressRunner(title, project, myMonitor);
myProcessor = new QueueProcessor<TaskData>((data, continuation) -> {
Task.Backgroundable task = data.task;
ProgressIndicator indicator = data.indicator;
ModalityState modalityState = data.modalityState;
myProcessor = new QueueProcessor<Pair<Task.Backgroundable, Getter<ProgressIndicator>>>(consumer, true,
threadToUse, new Condition<Object>() {
@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<ProgressIndicator> 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<Pair<Task.Backgroundable, Getter<ProgressIndicator>>, Runnable> {
@Override
public void consume(Pair<Task.Backgroundable, Getter<ProgressIndicator>> pair, Runnable runnable) {
final Task.Backgroundable backgroundable = pair.getFirst();
// synchronously
ProgressManager.getInstance().run(backgroundable);
runnable.run();
}
}
private static class BackgroundableUnderProgressRunner implements PairConsumer<Pair<Task.Backgroundable, Getter<ProgressIndicator>>, Runnable> {
private final String myTitle;
private final Project myProject;
private final PlusMinus<String> myMonitor;
public BackgroundableUnderProgressRunner(String title, final Project project, PlusMinus<String> monitor) {
myTitle = title;
myProject = project;
myMonitor = monitor;
}
@Override
public void consume(final Pair<Task.Backgroundable, Getter<ProgressIndicator>> 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;
}
});
}
}
}
@@ -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<String> {
private final static Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.BackgroundTasksMonitor");
private static final long ourStatInterval = 300000;
private long myRecentTime;
private final Map<String, Integer> myMap;
private final Map<String, Integer> myMaxMap;
private final Object myLock;
private final String myQueueTitle;
public BackgroundTasksMonitor(final String queueTitle) {
myQueueTitle = queueTitle;
myMap = new HashMap<String, Integer>();
myMaxMap = new HashMap<String, Integer>();
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<String, Integer> entry : myMap.entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue());
}
sb.append("\nMaximum Values:");
for (Map.Entry<String, Integer> entry : myMaxMap.entrySet()) {
sb.append('\n').append(entry.getKey()).append(": ").append(entry.getValue());
}
sb.append("----------------------------------------------------\n");
LOG.info(sb.toString());
myRecentTime = time;
}
}
@@ -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) {
@@ -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));
}
/**