add ProgressManager#computePrioritized to be able to speed up bg calculations during heavy activities like indexing

and use it in Goto File, Structure View and editor loading

this generalizes the approach from HeavyProcessLatch#prioritizeUiThread

GitOrigin-RevId: 4ae6510f1f8a4a887cb7d3c7e243adf76f5bd6f7
This commit is contained in:
peter
2019-05-29 18:07:35 +03:00
committed by intellij-monorepo-bot
parent 5a8170b90e
commit 56920bd51b
16 changed files with 234 additions and 152 deletions
@@ -248,4 +248,13 @@ public abstract class ProgressManager extends ProgressIndicatorProvider {
public abstract boolean runInReadActionWithWriteActionPriority(@NotNull final Runnable action, @Nullable ProgressIndicator indicator);
public abstract boolean isInNonCancelableSection();
/**
* Performs the given computation while giving more priority to the current thread
* (by forcing all other non-prioritized threads to sleep a bit whenever they call {@link #checkCanceled()}.<p></p>
*
* This is intended for relatively short (expected to be under 10 seconds) background activities that the user is waiting for
* (e.g. code navigation), and which shouldn't be slowed down by CPU-intensive background tasks like highlighting or indexing.
*/
public abstract <T, E extends Throwable> T computePrioritized(@NotNull ThrowableComputable<T, E> computable) throws E;
}
@@ -3,10 +3,12 @@ package com.intellij.openapi.progress.impl;
import com.google.common.collect.ConcurrentHashMultiset;
import com.intellij.concurrency.JobScheduler;
import com.intellij.diagnostic.ThreadDumper;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.*;
import com.intellij.openapi.project.Project;
@@ -14,8 +16,11 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.containers.ConcurrentLongObjectMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.SmartHashSet;
@@ -28,6 +33,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class CoreProgressManager extends ProgressManager implements Disposable {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.impl.CoreProgressManager");
@@ -674,6 +680,162 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
return isInNonCancelableSection.get() != null;
}
private static final long MAX_PRIORITIZATION_NANOS = TimeUnit.SECONDS.toNanos(12);
private static final Thread[] NO_THREADS = new Thread[0];
private final Set<Thread> myPrioritizedThreads = ContainerUtil.newConcurrentSet();
private volatile Thread[] myEffectivePrioritizedThreads = NO_THREADS;
private volatile int myDeprioritizations = 0;
private final Object myPrioritizationLock = ObjectUtils.sentinel("myPrioritizationLock");
private volatile long myPrioritizingStarted = 0;
@Override
public <T, E extends Throwable> T computePrioritized(@NotNull ThrowableComputable<T, E> computable) throws E {
Thread thread = Thread.currentThread();
if (!Registry.is("ide.prioritize.threads") || isPrioritizedThread(thread)) {
return computable.compute();
}
synchronized (myPrioritizationLock) {
if (myPrioritizedThreads.isEmpty()) {
myPrioritizingStarted = System.nanoTime();
}
myPrioritizedThreads.add(thread);
updateEffectivePrioritized();
}
try {
return computable.compute();
}
finally {
synchronized (myPrioritizationLock) {
myPrioritizedThreads.remove(thread);
updateEffectivePrioritized();
}
}
}
private void updateEffectivePrioritized() {
Thread[] prev = myEffectivePrioritizedThreads;
Thread[] current = myDeprioritizations > 0 || myPrioritizedThreads.isEmpty() ? NO_THREADS : myPrioritizedThreads.toArray(NO_THREADS);
myEffectivePrioritizedThreads = current;
if (prev.length == 0 && current.length > 0) {
prioritizingStarted();
} else if (prev.length > 0 && current.length == 0) {
prioritizingFinished();
}
}
protected void prioritizingStarted() {}
protected void prioritizingFinished() {}
@ApiStatus.Internal
public boolean isPrioritizedThread(@NotNull Thread from) {
return myPrioritizedThreads.contains(from);
}
@ApiStatus.Internal
public void suppressPrioritizing() {
synchronized (myPrioritizationLock) {
if (++myDeprioritizations == 100) {
Attachment attachment = new Attachment("threadDump.txt", ThreadDumper.dumpThreadsToString());
attachment.setIncluded(true);
LOG.error("A suspiciously high nesting of suppressPrioritizing, forgot to call restorePrioritizing?", attachment);
}
updateEffectivePrioritized();
}
}
@ApiStatus.Internal
public void restorePrioritizing() {
synchronized (myPrioritizationLock) {
if (--myDeprioritizations < 0) {
myDeprioritizations = 0;
LOG.error("Unmatched suppressPrioritizing/restorePrioritizing");
}
updateEffectivePrioritized();
}
}
protected boolean sleepIfNeededToGivePriorityToAnotherThread() {
if (!isCurrentThreadEffectivelyPrioritized() && checkLowPriorityReallyApplicable()) {
LockSupport.parkNanos(1_000_000);
avoidBlockingPrioritizingThread();
return true;
}
return false;
}
private boolean isCurrentThreadEffectivelyPrioritized() {
Thread current = Thread.currentThread();
for (Thread prioritized : myEffectivePrioritizedThreads) {
if (prioritized == current) {
return true;
}
}
return false;
}
private boolean checkLowPriorityReallyApplicable() {
long time = System.nanoTime() - myPrioritizingStarted;
if (time < 5_000_000) {
return false; // don't sleep when activities are very short (e.g. empty processing of mouseMoved events)
}
if (avoidBlockingPrioritizingThread()) {
return false;
}
if (ApplicationManager.getApplication().isDispatchThread()) {
return false; // EDT always has high priority
}
if (time > MAX_PRIORITIZATION_NANOS) {
// Don't wait forever in case someone forgot to stop prioritizing before waiting for other threads to complete
// wait just for 12 seconds; this will be noticeable (and we'll get 2 thread dumps) but not fatal
stopAllPrioritization();
return false;
}
return true;
}
private boolean avoidBlockingPrioritizingThread() {
if (isAnyPrioritizedThreadBlocked()) {
// the current thread could hold a lock that prioritized threads are waiting for
suppressPrioritizing();
checkLaterThreadsAreUnblocked();
return true;
}
return false;
}
private void checkLaterThreadsAreUnblocked() {
AppExecutorUtil.getAppScheduledExecutorService().schedule(() -> {
if (isAnyPrioritizedThreadBlocked()) {
checkLaterThreadsAreUnblocked();
}
else {
restorePrioritizing();
}
}, 5, TimeUnit.MILLISECONDS);
}
private void stopAllPrioritization() {
synchronized (myPrioritizationLock) {
myPrioritizedThreads.clear();
updateEffectivePrioritized();
}
}
private boolean isAnyPrioritizedThreadBlocked() {
for (Thread thread : myEffectivePrioritizedThreads) {
Thread.State state = thread.getState();
if (state == Thread.State.WAITING || state == Thread.State.TIMED_WAITING || state == Thread.State.BLOCKED) {
return true;
}
}
return false;
}
@NotNull
public static ModalityState getCurrentThreadProgressModality() {
ProgressIndicator indicator = threadTopLevelIndicators.get(Thread.currentThread().getId());
@@ -20,7 +20,6 @@
package com.intellij.psi.impl.source.tree;
import com.intellij.diagnostic.PluginException;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.LogUtil;
import com.intellij.openapi.diagnostic.Logger;
@@ -31,7 +30,6 @@ import com.intellij.psi.impl.DebugUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ILazyParseableElementTypeBase;
import com.intellij.reference.SoftReference;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.text.ImmutableCharSequence;
import org.jetbrains.annotations.NonNls;
@@ -177,12 +175,6 @@ public class LazyParseableElement extends CompositeElement {
}
if (myParsed) return;
if (ApplicationManager.getApplication().isDispatchThread()) {
// we don't want to wait under lock on EDT while another thread is parsing the same chameleon
// and sleeping in ProgressManagerImpl.sleepIfNeededToGivePriorityToAnotherThread because EDT is occupied
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
}
CharSequence text;
synchronized (lock) {
if (myParsed) return;
@@ -214,7 +206,7 @@ public class LazyParseableElement extends CompositeElement {
myText = new SoftReference<>(text);
});
}
}
}
private void assertTextLengthIntact(CharSequence text, TreeElement child) {
int length = 0;
@@ -50,7 +50,6 @@ import com.intellij.ui.components.JBList;
import com.intellij.util.CollectConsumer;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.accessibility.AccessibleContextUtil;
import com.intellij.util.ui.accessibility.ScreenReader;
@@ -1262,9 +1261,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable,
}
private <T> T withLock(Computable<T> computable) {
if (ApplicationManager.getApplication().isDispatchThread()) {
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
}
synchronized (myArrangerLock) {
return computable.compute();
}
@@ -35,6 +35,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
@@ -135,7 +136,8 @@ public class GotoDeclarationAction extends BaseCodeInsightAction implements Code
return ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
DumbService.getInstance(project).setAlternativeResolveEnabled(true);
try {
return ApplicationManager.getApplication().runReadAction(computable);
ThrowableComputable<T, RuntimeException> inRead = () -> ApplicationManager.getApplication().runReadAction(computable);
return ProgressManager.getInstance().computePrioritized(inRead);
}
finally {
DumbService.getInstance(project).setAlternativeResolveEnabled(false);
@@ -56,6 +56,13 @@ public class GotoFileItemProvider extends DefaultChooseByNameItemProvider {
@NotNull FindSymbolParameters parameters,
@NotNull ProgressIndicator indicator,
@NotNull Processor<Object> consumer) {
return ProgressManager.getInstance().computePrioritized(() -> doFilterElements(base, parameters, indicator, consumer));
}
private boolean doFilterElements(@NotNull ChooseByNameBase base,
@NotNull FindSymbolParameters parameters,
@NotNull ProgressIndicator indicator,
@NotNull Processor<Object> consumer) {
long start = System.currentTimeMillis();
try {
String pattern = parameters.getCompletePattern();
@@ -268,7 +275,7 @@ public class GotoFileItemProvider extends DefaultChooseByNameItemProvider {
}
boolean processName(String name) {
ProgressManager.checkCanceled();
indicator.checkCanceled();
int position = findMatchStartingPosition(name, namePattern);
if (position < namePattern.length()) {
candidateNames.get(position).add(name);
@@ -32,6 +32,7 @@ import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.*;
@@ -154,8 +155,11 @@ public class FileStructurePopup implements Disposable, TreeActionsOwner {
if (!ApplicationManager.getApplication().isUnitTestMode() && myPopup.isDisposed()) {
return;
}
super.rebuildTree();
myFilteringStructure.rebuild();
ProgressManager.getInstance().computePrioritized(() -> {
super.rebuildTree();
myFilteringStructure.rebuild();
return null;
});
}
@Override
@@ -51,8 +51,9 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameInScopeItemP
@NotNull FindSymbolParameters parameters,
@NotNull ProgressIndicator indicator,
@NotNull Processor<Object> consumer) {
return filterElements(base, indicator, myContext == null ? null : myContext.getElement(),
() -> base.getNames(parameters.isSearchInLibraries()), consumer, parameters);
return ProgressManager.getInstance().computePrioritized(
() -> filterElements(base, indicator, myContext == null ? null : myContext.getElement(),
() -> base.getNames(parameters.isSearchInLibraries()), consumer, parameters));
}
/**
@@ -7,10 +7,10 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.impl.CoreProgressManager;
import com.intellij.openapi.progress.util.StandardProgressIndicatorBase;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import com.intellij.util.io.storage.HeavyProcessLatch;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,10 +41,13 @@ public class JobLauncherImpl extends JobLauncher {
Boolean result = processImmediatelyIfTooFew(things, wrapper, runInReadAction, thingProcessor);
if (result != null) return result.booleanValue();
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
ProgressManager pm = ProgressManager.getInstance();
Processor<? super T> processor = ((CoreProgressManager)pm).isPrioritizedThread(Thread.currentThread())
? t -> pm.computePrioritized(() -> thingProcessor.process(t))
: thingProcessor;
List<ApplierCompleter<T>> failedSubTasks = Collections.synchronizedList(new ArrayList<>());
ApplierCompleter<T> applier = new ApplierCompleter<>(null, runInReadAction, failFastOnAcquireReadAction, wrapper, things, thingProcessor, 0, things.size(), failedSubTasks, null);
ApplierCompleter<T> applier = new ApplierCompleter<>(null, runInReadAction, failFastOnAcquireReadAction, wrapper, things, processor, 0, things.size(), failedSubTasks, null);
try {
ForkJoinPool.commonPool().execute(applier);
// call checkCanceled a bit more often than .invoke()
@@ -202,8 +205,6 @@ public class JobLauncherImpl extends JobLauncher {
// waits for the job to finish execution (when called on a canceled job in the middle of the execution, wait for finish)
@Override
public void waitForCompletion(int millis) throws InterruptedException, ExecutionException, TimeoutException {
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
while (!isDone()) {
try {
myForkJoinTask.get(millis, TimeUnit.MILLISECONDS);
@@ -14,6 +14,7 @@ import com.intellij.openapi.actionSystem.Shortcut;
import com.intellij.openapi.actionSystem.impl.ActionManagerImpl;
import com.intellij.openapi.application.*;
import com.intellij.openapi.application.impl.LaterInvocator;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.FrequentEventDetector;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.keymap.Keymap;
@@ -21,6 +22,7 @@ import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher;
import com.intellij.openapi.keymap.impl.IdeMouseEventDispatcher;
import com.intellij.openapi.keymap.impl.KeyState;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.ui.JBPopupMenu;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.registry.Registry;
@@ -36,7 +38,6 @@ import com.intellij.util.ReflectionUtil;
import com.intellij.util.SystemProperties;
import com.intellij.util.concurrency.NonUrgentExecutor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.lang.JavaVersion;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
@@ -72,6 +73,7 @@ public class IdeEventQueue extends EventQueue {
private static final Logger FOCUS_AWARE_RUNNABLES_LOG = Logger.getInstance("#com.intellij.ide.IdeEventQueue.runnables");
private static final boolean JAVA11_ON_MAC = SystemInfo.isMac && SystemInfo.isJavaVersionAtLeast(11, 0, 0);
private static TransactionGuardImpl ourTransactionGuard;
private static ProgressManager ourProgressManager;
/**
* Adding/Removing of "idle" listeners should be thread safe.
@@ -382,15 +384,21 @@ public class IdeEventQueue extends EventQueue {
AWTEvent oldEvent = myCurrentEvent;
myCurrentEvent = e;
HeavyProcessLatch.INSTANCE.prioritizeUiActivity();
try (AccessToken ignored = startActivity(e)) {
_dispatchEvent(e);
ProgressManager progressManager = obtainProgressManager();
if (progressManager != null) {
progressManager.computePrioritized(() -> {
_dispatchEvent(myCurrentEvent);
return null;
});
} else {
_dispatchEvent(myCurrentEvent);
}
}
catch (Throwable t) {
processException(t);
}
finally {
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
myIsInInputEvent = wasInputEvent;
myCurrentEvent = oldEvent;
@@ -433,6 +441,15 @@ public class IdeEventQueue extends EventQueue {
}
}
@Nullable
private static ProgressManager obtainProgressManager() {
ProgressManager manager = ourProgressManager;
if (manager == null) {
ourProgressManager = manager = ServiceManager.getService(ProgressManager.class);
}
return manager;
}
private static boolean isMetaKeyPressedOnLinux(@NotNull AWTEvent e) {
if (!Registry.is("keymap.skip.meta.press.on.linux")) return false;
boolean metaIsPressed = e instanceof InputEvent && (((InputEvent)e).getModifiersEx() & InputEvent.META_DOWN_MASK) != 0;
@@ -507,7 +507,6 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
@Override
public void dispose() {
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
fireApplicationExiting();
ShutDownTracker.getInstance().ensureStopperThreadsFinished();
@@ -1184,7 +1183,6 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
if (!isWriteAccessAllowed()) {
assertIsDispatchThread("Write access is allowed from event dispatch thread only");
}
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing(); // let non-cancellable read actions complete faster, if present
boolean writeActionPending = myWriteActionPending;
if (gatherStatistics && myWriteActionsStack.isEmpty() && !writeActionPending) {
ActionPauses.WRITE.started();
@@ -85,7 +85,7 @@ public class AsyncEditorLoader {
ReadAction
.nonBlocking(() -> {
waitForCommit(commitDeadline);
Runnable runnable = myTextEditor.loadEditorInBackground();
Runnable runnable = ProgressManager.getInstance().computePrioritized(() -> myTextEditor.loadEditorInBackground());
future.complete(runnable);
return runnable;
})
@@ -13,9 +13,7 @@ import com.intellij.openapi.progress.util.ProgressWindow;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.ui.SystemNotifications;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.storage.HeavyProcessLatch;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
@@ -24,38 +22,13 @@ import javax.swing.*;
import java.awt.*;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
public class ProgressManagerImpl extends CoreProgressManager implements Disposable {
private static final Key<Boolean> SAFE_PROGRESS_INDICATOR = Key.create("SAFE_PROGRESS_INDICATOR");
private final Set<CheckCanceledHook> myHooks = ContainerUtil.newConcurrentSet();
private final CheckCanceledHook mySleepHook = __ -> sleepIfNeededToGivePriorityToAnotherThread();
public ProgressManagerImpl() {
HeavyProcessLatch.INSTANCE.addUIActivityListener(new HeavyProcessLatch.HeavyProcessListener() {
private final CheckCanceledHook sleepHook = indicator -> sleepIfNeededToGivePriorityToAnotherThread();
private final AtomicBoolean scheduled = new AtomicBoolean();
private final Runnable addHookLater = () -> {
scheduled.set(false);
if (HeavyProcessLatch.INSTANCE.hasPrioritizedThread()) {
addCheckCanceledHook(sleepHook);
}
};
@Override
public void processStarted() {
if (scheduled.compareAndSet(false, true)) {
AppExecutorUtil.getAppScheduledExecutorService().schedule(addHookLater, 5, TimeUnit.MILLISECONDS);
}
}
@Override
public void processFinished() {
removeCheckCanceledHook(sleepHook);
}
}, this);
ExtensionPointImpl.setCheckCanceledAction(ProgressManager::checkCanceled);
}
@@ -183,11 +156,13 @@ public class ProgressManagerImpl extends CoreProgressManager implements Disposab
};
}
private static boolean sleepIfNeededToGivePriorityToAnotherThread() {
if (HeavyProcessLatch.INSTANCE.isInsideLowPriorityThread()) {
LockSupport.parkNanos(1_000_000);
return true;
}
return false;
@Override
protected void prioritizingStarted() {
addCheckCanceledHook(mySleepHook);
}
@Override
protected void prioritizingFinished() {
removeCheckCanceledHook(mySleepHook);
}
}
@@ -14,6 +14,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diagnostic.RuntimeExceptionWithAttachments;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.progress.*;
import com.intellij.openapi.progress.impl.CoreProgressManager;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.progress.impl.ProgressSuspender;
import com.intellij.openapi.progress.util.AbstractProgressIndicatorExBase;
@@ -259,15 +260,13 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
if (indicator == null) indicator = new EmptyProgressIndicator();
if (ApplicationManager.getApplication().isDispatchThread()) {
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
}
indicator.pushState();
((CoreProgressManager)ProgressManager.getInstance()).suppressPrioritizing();
try (AccessToken ignored = HeavyProcessLatch.INSTANCE.processStarted("Performing indexing task")) {
task.performInDumbMode(indicator);
}
finally {
((CoreProgressManager)ProgressManager.getInstance()).restorePrioritizing();
indicator.popState();
Disposer.dispose(task);
}
@@ -1349,8 +1349,8 @@ idea.regexp.search.uses.indices.description=Should IDE find in path use indices
idea.io.safe.sync=true
idea.io.safe.sync.description=When "Safe Write" is enabled, sync() is invoked after the last write to flush data to disk
ide.prioritize.ui.thread=true
ide.prioritize.ui.thread.description=In presence of UI activity, deprioritizes all other threads for the activity to complete ASAP
ide.prioritize.threads=true
ide.prioritize.threads.description=In presence of UI activity, deprioritizes all other threads for the activity to complete ASAP
ide.require.transaction.for.model.changes=true
ide.require.transaction.for.model.changes.description=Whether write action can only happen under TransactionGuard
@@ -22,12 +22,10 @@ package com.intellij.util.io.storage;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.util.EventDispatcher;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.*;
public class HeavyProcessLatch {
@@ -37,15 +35,6 @@ public class HeavyProcessLatch {
private final Set<String> myHeavyProcesses = new THashSet<>();
private final EventDispatcher<HeavyProcessListener> myEventDispatcher = EventDispatcher.create(HeavyProcessListener.class);
private final EventDispatcher<HeavyProcessListener> myUIProcessDispatcher = EventDispatcher.create(HeavyProcessListener.class);
private volatile Thread myUiActivityThread;
/**
Don't wait forever in case someone forgot to stop prioritizing before waiting for other threads to complete
wait just for 12 seconds; this will be noticeable (and we'll get 2 thread dumps) but not fatal
*/
private static final int MAX_PRIORITIZATION_MILLIS = 12 * 1000;
private volatile long myPrioritizingStarted;
private final List<Runnable> toExecuteOutOfHeavyActivity = new ArrayList<>();
private HeavyProcessLatch() {
@@ -113,11 +102,6 @@ public class HeavyProcessLatch {
myEventDispatcher.addListener(listener, parentDisposable);
}
public void addUIActivityListener(@NotNull HeavyProcessListener listener,
@NotNull Disposable parentDisposable) {
myUIProcessDispatcher.addListener(listener, parentDisposable);
}
public void executeOutOfHeavyProcess(@NotNull Runnable runnable) {
boolean runNow;
synchronized (myHeavyProcesses) {
@@ -134,69 +118,4 @@ public class HeavyProcessLatch {
}
}
/**
* Gives current event processed on Swing thread higher priority
* by letting other threads to sleep a bit whenever they call checkCanceled.<p></p>
*
* Don't call this method unless you're deep in Swing event dispatching internals.
* @see #stopThreadPrioritizing()
*/
public void prioritizeUiActivity() {
LOG.assertTrue(SwingUtilities.isEventDispatchThread());
if (!Registry.is("ide.prioritize.ui.thread", false)) {
return;
}
myPrioritizingStarted = System.currentTimeMillis();
myUiActivityThread = Thread.currentThread();
myUIProcessDispatcher.getMulticaster().processStarted();
}
/**
* Removes priority from Swing thread, if present. Should be invoked before UI thread starts waiting for other threads in idle mode,
* to ensure those other threads complete ASAP.<p></p>
*
* If possible, instead of calling this method prefer to move activity into background threads,
* so that UI thread doesn't need to block and wait.
*/
public void stopThreadPrioritizing() {
if (myUiActivityThread == null) return;
myUiActivityThread = null;
myUIProcessDispatcher.getMulticaster().processFinished();
}
/**
* @return whether there is a prioritized thread, but not the current one
*/
public boolean isInsideLowPriorityThread() {
Thread uiThread = myUiActivityThread;
if (uiThread != null && uiThread != Thread.currentThread()) {
Thread.State state = uiThread.getState();
if (state == Thread.State.WAITING || state == Thread.State.TIMED_WAITING || state == Thread.State.BLOCKED) {
return false;
}
long time = System.currentTimeMillis() - myPrioritizingStarted;
if (time < 5) {
return false; // don't sleep when EDT activities are very short (e.g. empty processing of mouseMoved events)
}
if (time > MAX_PRIORITIZATION_MILLIS) {
stopThreadPrioritizing();
return false;
}
return true;
}
return false;
}
/**
* @return whether there is a prioritized thread currently
*/
public boolean hasPrioritizedThread() {
return myUiActivityThread != null;
}
}