mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
deprioritize other threads when a user performs a UI activity (disabled for now)
This commit is contained in:
@@ -27,10 +27,13 @@ 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.TimeoutUtil;
|
||||
import com.intellij.util.containers.ConcurrentLongObjectMap;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.SmartHashSet;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -51,6 +54,7 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
|
||||
private final AtomicInteger myCurrentModalProgressCount = new AtomicInteger(0);
|
||||
|
||||
private static final boolean ENABLED = !"disabled".equals(System.getProperty("idea.ProcessCanceledException"));
|
||||
private static final boolean ourMaySleepInCheckCanceled = Registry.is("ide.prioritize.ui.thread", false);
|
||||
private ScheduledFuture<?> myCheckCancelledFuture; // guarded by threadsUnderIndicator
|
||||
|
||||
// indicator -> threads which are running under this indicator. guarded by threadsUnderIndicator.
|
||||
@@ -59,7 +63,7 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
|
||||
private static final ConcurrentLongObjectMap<ProgressIndicator> currentIndicators = ContainerUtil.createConcurrentLongObjectMap();
|
||||
// threads which are running under canceled indicator
|
||||
static final Set<Thread> threadsUnderCanceledIndicator = ContainerUtil.newConcurrentSet();
|
||||
private static volatile boolean thereIsProcessUnderCanceledIndicator;
|
||||
private static volatile boolean shouldCheckCanceled;
|
||||
|
||||
/** active (i.e. which have {@link #executeProcessUnderProgress(Runnable, ProgressIndicator)} method running) indicators
|
||||
* which are not inherited from {@link StandardProgressIndicator}.
|
||||
@@ -68,6 +72,21 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
|
||||
*/
|
||||
private static final Collection<ProgressIndicator> nonStandardIndicators = ConcurrentHashMultiset.create();
|
||||
|
||||
public CoreProgressManager() {
|
||||
HeavyProcessLatch.INSTANCE.addListener(this, new HeavyProcessLatch.HeavyProcessListener() {
|
||||
@Override
|
||||
public void processStarted() {
|
||||
updateShouldCheckCanceled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processFinished() {
|
||||
updateShouldCheckCanceled();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ScheduledFuture<?> startBackgroundIndicatorPing() {
|
||||
return JobScheduler.getScheduler().scheduleWithFixedDelay(new Runnable() {
|
||||
@@ -95,13 +114,24 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean sleepIfNeeded() {
|
||||
if (ourMaySleepInCheckCanceled && HeavyProcessLatch.INSTANCE.isInsideLowPriorityThread()) {
|
||||
TimeoutUtil.sleep(1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCheckCanceled() throws ProcessCanceledException {
|
||||
if (thereIsProcessUnderCanceledIndicator) {
|
||||
final ProgressIndicator progress = getProgressIndicator();
|
||||
if (progress != null && ENABLED) {
|
||||
progress.checkCanceled();
|
||||
}
|
||||
if (!shouldCheckCanceled) return;
|
||||
|
||||
final ProgressIndicator progress = getProgressIndicator();
|
||||
if (progress != null && ENABLED) {
|
||||
progress.checkCanceled();
|
||||
}
|
||||
else {
|
||||
sleepIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +232,7 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
|
||||
}
|
||||
}, null);
|
||||
|
||||
//noinspection ThrowableResultOfMethodCallIgnored
|
||||
Throwable t = exception.get();
|
||||
if (t != null) {
|
||||
if (t instanceof Error) throw (Error)t;
|
||||
@@ -434,7 +465,7 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
thereIsProcessUnderCanceledIndicator = !threadsUnderCanceledIndicator.isEmpty();
|
||||
updateShouldCheckCanceled();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -467,11 +498,22 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
|
||||
threadsUnderCanceledIndicator.remove(currentThread);
|
||||
}
|
||||
}
|
||||
thereIsProcessUnderCanceledIndicator = !threadsUnderCanceledIndicator.isEmpty();
|
||||
updateShouldCheckCanceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateShouldCheckCanceled() {
|
||||
if (ourMaySleepInCheckCanceled && HeavyProcessLatch.INSTANCE.hasPrioritizedThread()) {
|
||||
shouldCheckCanceled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (threadsUnderIndicator) {
|
||||
shouldCheckCanceled = !threadsUnderCanceledIndicator.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void indicatorCanceled(@NotNull ProgressIndicator indicator) {
|
||||
// mark threads running under this indicator as canceled
|
||||
@@ -492,7 +534,8 @@ public class CoreProgressManager extends ProgressManager implements Disposable {
|
||||
|
||||
if (underCancelledIndicator) {
|
||||
threadsUnderCanceledIndicator.add(thread);
|
||||
thereIsProcessUnderCanceledIndicator = true;
|
||||
//noinspection AssignmentToStaticFieldFromInstanceMethod
|
||||
shouldCheckCanceled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -22,6 +22,7 @@ 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.util.UserDataHolderBase;
|
||||
import com.intellij.ui.mac.foundation.MacUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -34,6 +35,7 @@ import java.util.Set;
|
||||
|
||||
public class AbstractProgressIndicatorBase extends UserDataHolderBase implements ProgressIndicatorStacked {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.progress.util.ProgressIndicatorBase");
|
||||
private static final CoreProgressManager ourProgressManager = (CoreProgressManager)ProgressManager.getInstance();
|
||||
|
||||
private volatile String myText;
|
||||
private volatile double myFraction;
|
||||
@@ -126,6 +128,11 @@ public class AbstractProgressIndicatorBase extends UserDataHolderBase implements
|
||||
if (isCanceled() && isCancelable()) {
|
||||
throw new ProcessCanceledException();
|
||||
}
|
||||
if (ourProgressManager.sleepIfNeeded()) {
|
||||
if (isCanceled() && isCancelable()) {
|
||||
throw new ProcessCanceledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import jsr166e.ForkJoinPool;
|
||||
import jsr166e.ForkJoinTask;
|
||||
import jsr166e.ForkJoinWorkerThread;
|
||||
@@ -96,6 +97,8 @@ public class JobLauncherImpl extends JobLauncher {
|
||||
Boolean result = processImmediatelyIfTooFew(things, wrapper, runInReadAction, thingProcessor);
|
||||
if (result != null) return result.booleanValue();
|
||||
|
||||
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
|
||||
|
||||
ApplierCompleter<T> applier = new ApplierCompleter<>(null, runInReadAction, wrapper, things, thingProcessor, 0, things.size(), null);
|
||||
try {
|
||||
pool.invoke(applier);
|
||||
@@ -269,6 +272,8 @@ 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);
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.intellij.util.Alarm;
|
||||
import com.intellij.util.ReflectionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import com.intellij.util.ui.MouseEventAdapter;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -379,6 +380,9 @@ public class IdeEventQueue extends EventQueue {
|
||||
|
||||
boolean wasInputEvent = myIsInInputEvent;
|
||||
myIsInInputEvent = e instanceof InputEvent || e instanceof InputMethodEvent || e instanceof WindowEvent || e instanceof ActionEvent;
|
||||
if (myIsInInputEvent) {
|
||||
HeavyProcessLatch.INSTANCE.prioritizeUiActivity();
|
||||
}
|
||||
AWTEvent oldEvent = myCurrentEvent;
|
||||
myCurrentEvent = e;
|
||||
|
||||
|
||||
@@ -544,6 +544,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing();
|
||||
fireApplicationExiting();
|
||||
|
||||
ShutDownTracker.getInstance().ensureStopperThreadsFinished();
|
||||
@@ -1221,6 +1222,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
|
||||
private void startWrite(/*@NotNull*/ Class clazz) {
|
||||
assertIsDispatchThread(getStatus(), "Write access is allowed from event dispatch thread only");
|
||||
HeavyProcessLatch.INSTANCE.stopThreadPrioritizing(); // let non-cancellable read actions complete faster, if present
|
||||
boolean writeActionPending = myWriteActionPending;
|
||||
myWriteActionPending = true;
|
||||
if (gatherWriteActionStatistics && myWriteActionsStack.isEmpty()) {
|
||||
@@ -1435,6 +1437,7 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
if (myDoNotSave) return;
|
||||
|
||||
if (mySaveSettingsIsInProgress.compareAndSet(false, true)) {
|
||||
HeavyProcessLatch.INSTANCE.prioritizeUiActivity();
|
||||
try {
|
||||
StoreUtil.save(ServiceKt.getStateStore(this), null);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbAwareRunnable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
@@ -47,6 +48,7 @@ import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.openapi.wm.impl.FrameTitleBuilder;
|
||||
import com.intellij.psi.impl.DebugUtil;
|
||||
import com.intellij.util.TimedReference;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import com.intellij.util.pico.ConstructorInjectionComponentAdapter;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -310,6 +312,8 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project
|
||||
return;
|
||||
}
|
||||
|
||||
HeavyProcessLatch.INSTANCE.prioritizeUiActivity();
|
||||
|
||||
try {
|
||||
StoreUtil.save(ServiceKt.getStateStore(this), this);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.ex.ApplicationInfoEx;
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbAwareRunnable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
@@ -52,6 +53,7 @@ import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.mac.MacMainFrameDecorator;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -260,6 +262,8 @@ public class IdeFrameImpl extends JFrame implements IdeFrameEx, DataProvider {
|
||||
final Application app = ApplicationManager.getApplication();
|
||||
app.invokeLater(new DumbAwareRunnable() {
|
||||
public void run() {
|
||||
HeavyProcessLatch.INSTANCE.prioritizeUiActivity();
|
||||
|
||||
if (app.isDisposed()) {
|
||||
ApplicationManagerEx.getApplicationEx().exit();
|
||||
return;
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.util.ActionCallback;
|
||||
import com.intellij.util.io.storage.HeavyProcessLatch;
|
||||
import com.intellij.util.ui.JBInsets;
|
||||
|
||||
import javax.accessibility.Accessible;
|
||||
@@ -130,6 +131,7 @@ public abstract class CardLayoutPanel<K, UI, V extends Component> extends JCompo
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
HeavyProcessLatch.INSTANCE.prioritizeUiActivity();
|
||||
if (!myDisposed) {
|
||||
select(callback, key, ui);
|
||||
}
|
||||
|
||||
@@ -710,5 +710,8 @@ idea.regexp.search.uses.indices.description=Should Idea 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=false
|
||||
ide.prioritize.ui.thread.description=In presence of UI activity, deprioritizes all other threads for the activity to complete ASAP. Changing requires restart.
|
||||
|
||||
dumb.aware.run.configurations=false
|
||||
dumb.aware.run.configurations.description=Enable executing run configurations in dumb mode
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2016 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.
|
||||
@@ -25,14 +25,17 @@ import com.intellij.util.EventDispatcher;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.EventListener;
|
||||
import java.util.Set;
|
||||
|
||||
public class HeavyProcessLatch {
|
||||
public static final HeavyProcessLatch INSTANCE = new HeavyProcessLatch();
|
||||
private static final String UI_ACTIVITY = "UI Activity";
|
||||
|
||||
private final Set<String> myHeavyProcesses = new THashSet<String>();
|
||||
private final EventDispatcher<HeavyProcessListener> myEventDispatcher = EventDispatcher.create(HeavyProcessListener.class);
|
||||
private volatile Thread myUiActivityThread;
|
||||
|
||||
private HeavyProcessLatch() {
|
||||
}
|
||||
@@ -95,4 +98,45 @@ public class HeavyProcessLatch {
|
||||
public void addListener(@NotNull Disposable parentDisposable, @NotNull HeavyProcessListener listener) {
|
||||
myEventDispatcher.addListener(listener, parentDisposable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives current event processed on Swing thread higher priority
|
||||
* @see #stopThreadPrioritizing()
|
||||
*/
|
||||
public void prioritizeUiActivity() {
|
||||
myUiActivityThread = Thread.currentThread();
|
||||
processStarted(UI_ACTIVITY);
|
||||
//noinspection SSBasedInspection
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopThreadPrioritizing();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes priority from Swing thread, if present. Should be invoked before a thread starts waiting for other threads in idle mode,
|
||||
* to ensure those other threads complete ASAP.
|
||||
* @see #prioritizeUiActivity()
|
||||
*/
|
||||
public void stopThreadPrioritizing() {
|
||||
myUiActivityThread = null;
|
||||
processFinished(UI_ACTIVITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether there is a prioritized thread, but not the current one
|
||||
*/
|
||||
public boolean isInsideLowPriorityThread() {
|
||||
Thread thread = myUiActivityThread;
|
||||
return thread != null && thread != Thread.currentThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether there is a prioritized thread currently
|
||||
*/
|
||||
public boolean hasPrioritizedThread() {
|
||||
return myUiActivityThread != null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user