conservative fix for IDEA-132991

This commit is contained in:
Maxim.Mossienko
2014-11-21 20:44:11 +01:00
parent fc452a8fc4
commit 5515468ade
6 changed files with 167 additions and 114 deletions
@@ -125,4 +125,9 @@ public class MockApplicationEx extends MockApplication implements ApplicationEx
public boolean isWriteActionInProgress() {
return false;
}
@Override
public boolean isWriteActionPending() {
return false;
}
}
@@ -55,6 +55,12 @@ public interface ApplicationEx extends Application {
*/
boolean isWriteActionInProgress();
/**
* @return true if the EDT started to acquire write action but has not acquired it yet.
* @see #runWriteAction(Runnable)
*/
boolean isWriteActionPending();
void doNotSave();
void doNotSave(boolean value);
boolean isDoNotSave();
@@ -19,14 +19,13 @@
*/
package com.intellij.ui;
import com.intellij.concurrency.Job;
import com.intellij.concurrency.JobLauncher;
import com.intellij.ide.PowerSaveMode;
import com.intellij.openapi.application.ApplicationAdapter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.registry.Registry;
@@ -100,71 +99,54 @@ public class DeferredIconImpl<T> implements DeferredIcon {
final Component paintingParent = SwingUtilities.getAncestorOfClass(PaintingParent.class, c);
final Rectangle paintingParentRec = paintingParent == null ? null : ((PaintingParent)paintingParent).getChildRec(c);
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
JobLauncher.getInstance().submitToJobThread(Job.DEFAULT_PRIORITY, new Runnable() {
@Override
public void run() {
int oldWidth = myDelegateIcon.getIconWidth();
final Icon[] evaluated = new Icon[1];
final Runnable evalRunnable = new Runnable() {
@Override
public void run() {
try {
evaluated[0] = nonNull(myEvaluator.fun(myParam));
}
catch (ProcessCanceledException e) {
evaluated[0] = EMPTY_ICON;
}
catch (IndexNotReadyException e) {
evaluated[0] = EMPTY_ICON;
}
}
};
final long startTime = System.currentTimeMillis();
if (myNeedReadAction) {
final ProgressIndicatorBase progress = new ProgressIndicatorBase();
final ApplicationAdapter listener = new ApplicationAdapter() {
@Override
public void beforeWriteActionStart(Object action) {
progress.cancel();
}
};
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
final Ref<Boolean> cancelled = new Ref<Boolean>();
boolean result = ProgressIndicatorUtils.runWithWriteActionPriority(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().addApplicationListener(listener);
}
}, ModalityState.any());
try {
final Ref<Boolean> cancelled = new Ref<Boolean>();
ProgressManager.getInstance().runProcess(new Runnable() {
@Override
public void run() {
if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(new Runnable() {
@Override
public void run() {
IconDeferrerImpl.evaluateDeferred(evalRunnable);
if (myAutoUpdatable) {
myLastCalcTime = System.currentTimeMillis();
myLastTimeSpent = myLastCalcTime - startTime;
if (!ApplicationManagerEx.getApplicationEx().tryRunReadAction(new Runnable() {
@Override
public void run() {
IconDeferrerImpl.evaluateDeferred(new Runnable() {
@Override
public void run() {
try {
evaluated[0] = nonNull(myEvaluator.fun(myParam));
}
catch (IndexNotReadyException e) {
evaluated[0] = EMPTY_ICON;
}
}
});
if (myAutoUpdatable) {
myLastCalcTime = System.currentTimeMillis();
myLastTimeSpent = myLastCalcTime - startTime;
}
})) {
myIsScheduled = false;
cancelled.set(Boolean.TRUE);
}
})) {
cancelled.set(Boolean.TRUE);
}
}, progress);
if (cancelled.get() == Boolean.TRUE) return;
}
catch (ProcessCanceledException e) {
}
finally {
ApplicationManager.getApplication().removeApplicationListener(listener);
}
});
if (cancelled.get() == Boolean.TRUE || !result) {
myIsScheduled = false;
return;
}
}
else {
IconDeferrerImpl.evaluateDeferred(evalRunnable);
IconDeferrerImpl.evaluateDeferred(new Runnable() {
@Override
public void run() {
evaluated[0] = nonNull(myEvaluator.fun(myParam));
}
});
if (myAutoUpdatable) {
myLastCalcTime = System.currentTimeMillis();
myLastTimeSpent = myLastCalcTime - startTime;
@@ -1155,35 +1155,50 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
return new ReadAccessToken(status);
}
private volatile boolean myWriteActionPending;
@Override
public boolean isWriteActionPending() {
return myWriteActionPending;
}
private void startWrite(Class clazz) {
ActivityTracker.getInstance().inc();
fireBeforeWriteActionStart(clazz);
boolean writeActionPending = myWriteActionPending;
myWriteActionPending = true;
try {
if (!isWriteAccessAllowed()) {
assertNoPsiLock();
}
if (!myLock.writeLock().tryLock()) {
final AtomicBoolean lockAcquired = new AtomicBoolean(false);
if (ourDumpThreadsOnLongWriteActionWaiting > 0) {
executeOnPooledThread(new Runnable() {
@Override
public void run() {
while (!lockAcquired.get()) {
TimeoutUtil.sleep(ourDumpThreadsOnLongWriteActionWaiting);
if (!lockAcquired.get()) {
PerformanceWatcher.getInstance().dumpThreads(true);
ActivityTracker.getInstance().inc();
fireBeforeWriteActionStart(clazz);
try {
if (!isWriteAccessAllowed()) {
assertNoPsiLock();
}
if (!myLock.writeLock().tryLock()) {
final AtomicBoolean lockAcquired = new AtomicBoolean(false);
if (ourDumpThreadsOnLongWriteActionWaiting > 0) {
executeOnPooledThread(new Runnable() {
@Override
public void run() {
while (!lockAcquired.get()) {
TimeoutUtil.sleep(ourDumpThreadsOnLongWriteActionWaiting);
if (!lockAcquired.get()) {
PerformanceWatcher.getInstance().dumpThreads(true);
}
}
}
}
});
});
}
myLock.writeLock().lockInterruptibly();
lockAcquired.set(true);
}
myLock.writeLock().lockInterruptibly();
lockAcquired.set(true);
}
catch (InterruptedException e) {
throw new RuntimeInterruptedException(e);
}
}
catch (InterruptedException e) {
throw new RuntimeInterruptedException(e);
finally {
myWriteActionPending = writeActionPending;
}
myWriteActionsStack.push(clazz);
@@ -19,9 +19,11 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationAdapter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Ref;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.ide.PooledThreadExecutor;
@@ -61,6 +63,60 @@ public class ProgressIndicatorUtils {
scheduleWithWriteActionPriority(progressIndicator, PooledThreadExecutor.INSTANCE, readTask);
}
public static boolean runWithWriteActionPriority(@NotNull final Runnable action) {
return runWithWriteActionPriority(action, new ProgressIndicatorBase());
}
public static boolean runWithWriteActionPriority(@NotNull final Runnable action,
@NotNull final ProgressIndicator progressIndicator) {
final ApplicationEx application = (ApplicationEx)ApplicationManager.getApplication();
if (application.isWriteActionPending()) {
// first catch: check if write action acquisition started: especially important when current thread has read action, because
// tryRunReadAction below would just run without really checking if a write action is pending
if (!progressIndicator.isCanceled()) progressIndicator.cancel();
return false;
}
final ApplicationAdapter listener = new ApplicationAdapter() {
@Override
public void beforeWriteActionStart(Object action) {
if (!progressIndicator.isCanceled()) progressIndicator.cancel();
}
};
boolean succeededWithAddingListener = application.tryRunReadAction(new Runnable() {
@Override
public void run() {
// Even if writeLock.lock() acquisition is in progress at this point then runProcess will block wanting read action which is
// also ok as last resort.
application.addApplicationListener(listener);
}
});
if (!succeededWithAddingListener) { // second catch: writeLock.lock() acquisition is in progress or already acquired
if (!progressIndicator.isCanceled()) progressIndicator.cancel();
return false;
}
final Ref<Boolean> wasCancelled = new Ref<Boolean>();
try {
ProgressManager.getInstance().runProcess(new Runnable() {
@Override
public void run() {
try {
action.run();
}
catch (ProcessCanceledException ignore) {
wasCancelled.set(Boolean.TRUE);
}
}
}, progressIndicator);
}
finally {
application.removeApplicationListener(listener);
}
return wasCancelled.get() != Boolean.TRUE;
}
public static void scheduleWithWriteActionPriority(@NotNull final ProgressIndicator progressIndicator,
@NotNull final Executor executor,
@NotNull final ReadTask readTask) {
@@ -19,15 +19,14 @@ import com.intellij.ide.IdeBundle;
import com.intellij.ide.caches.CacheUpdater;
import com.intellij.ide.caches.FileContent;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationAdapter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
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.util.ProgressIndicatorBase;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.progress.util.ProgressWrapper;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.registry.Registry;
@@ -142,56 +141,46 @@ public class CacheUpdateRunner extends DumbModeTask {
}
}
private static boolean processSomeFilesWhileUserIsInactive(@NotNull FileContentQueue queue,
@NotNull Consumer<VirtualFile> progressUpdater,
private static boolean processSomeFilesWhileUserIsInactive(@NotNull final FileContentQueue queue,
@NotNull final Consumer<VirtualFile> progressUpdater,
final boolean processInReadAction,
@NotNull Project project,
@NotNull Consumer<FileContent> fileProcessor) {
@NotNull final Project project,
@NotNull final Consumer<FileContent> fileProcessor) {
final ProgressIndicatorBase innerIndicator = new ProgressIndicatorBase() {
@Override
protected boolean isCancelable() {
return true; // the inner indicator must be always cancelable
}
};
final ApplicationAdapter canceller = new ApplicationAdapter() {
@Override
public void beforeWriteActionStart(Object action) {
innerIndicator.cancel();
}
};
final Application application = ApplicationManager.getApplication();
application.invokeAndWait(new Runnable() {
final AtomicBoolean isFinished = new AtomicBoolean();
ProgressIndicatorUtils.runWithWriteActionPriority(new Runnable() {
@Override
public void run() {
application.addApplicationListener(canceller);
}
}, ModalityState.any());
final AtomicBoolean isFinished = new AtomicBoolean();
try {
int threadsCount = Registry.intValue("caches.indexerThreadsCount");
if (threadsCount <= 0) {
threadsCount = Math.max(1, Math.min(PROC_COUNT - 1, 4));
}
if (threadsCount == 1 || application.isWriteAccessAllowed()) {
Runnable process = new MyRunnable(innerIndicator, queue, isFinished, progressUpdater, processInReadAction, project, fileProcessor);
ProgressManager.getInstance().runProcess(process, innerIndicator);
}
else {
AtomicBoolean[] finishedRefs = new AtomicBoolean[threadsCount];
Future<?>[] futures = new Future<?>[threadsCount];
for (int i = 0; i < threadsCount; i++) {
AtomicBoolean ref = new AtomicBoolean();
finishedRefs[i] = ref;
Runnable process = new MyRunnable(innerIndicator, queue, ref, progressUpdater, processInReadAction, project, fileProcessor);
futures[i] = ApplicationManager.getApplication().executeOnPooledThread(process);
int threadsCount = Registry.intValue("caches.indexerThreadsCount");
if (threadsCount <= 0) {
threadsCount = Math.max(1, Math.min(PROC_COUNT - 1, 4));
}
if (threadsCount == 1 || application.isWriteAccessAllowed()) {
Runnable process =
new MyRunnable(innerIndicator, queue, isFinished, progressUpdater, processInReadAction, project, fileProcessor);
ProgressManager.getInstance().runProcess(process, innerIndicator);
}
else {
AtomicBoolean[] finishedRefs = new AtomicBoolean[threadsCount];
Future<?>[] futures = new Future<?>[threadsCount];
for (int i = 0; i < threadsCount; i++) {
AtomicBoolean ref = new AtomicBoolean();
finishedRefs[i] = ref;
Runnable process = new MyRunnable(innerIndicator, queue, ref, progressUpdater, processInReadAction, project, fileProcessor);
futures[i] = ApplicationManager.getApplication().executeOnPooledThread(process);
}
isFinished.set(waitForAll(finishedRefs, futures));
}
isFinished.set(waitForAll(finishedRefs, futures));
}
}
finally {
application.removeApplicationListener(canceller);
}
}, innerIndicator);
return isFinished.get();
}