diff --git a/platform/core-impl/src/com/intellij/mock/MockApplicationEx.java b/platform/core-impl/src/com/intellij/mock/MockApplicationEx.java index 3a3ea6e8e0d7..651ac40ddf22 100644 --- a/platform/core-impl/src/com/intellij/mock/MockApplicationEx.java +++ b/platform/core-impl/src/com/intellij/mock/MockApplicationEx.java @@ -125,4 +125,9 @@ public class MockApplicationEx extends MockApplication implements ApplicationEx public boolean isWriteActionInProgress() { return false; } + + @Override + public boolean isWriteActionPending() { + return false; + } } diff --git a/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationEx.java b/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationEx.java index 2b54053ae74b..8598b505de52 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationEx.java +++ b/platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationEx.java @@ -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(); diff --git a/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java b/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java index e49bea6c1732..215112edd826 100644 --- a/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java +++ b/platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java @@ -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 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 cancelled = new Ref(); + boolean result = ProgressIndicatorUtils.runWithWriteActionPriority(new Runnable() { @Override public void run() { - ApplicationManager.getApplication().addApplicationListener(listener); - } - }, ModalityState.any()); - try { - final Ref cancelled = new Ref(); - 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; diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index ff073cc2c619..ce21a68a4420 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -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); diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java index 05f614637b0a..36a70b1a5213 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressIndicatorUtils.java @@ -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 wasCancelled = new Ref(); + 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) { diff --git a/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateRunner.java b/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateRunner.java index 5541e8890f5d..9dbfd87aeb98 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateRunner.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateRunner.java @@ -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 progressUpdater, + private static boolean processSomeFilesWhileUserIsInactive(@NotNull final FileContentQueue queue, + @NotNull final Consumer progressUpdater, final boolean processInReadAction, - @NotNull Project project, - @NotNull Consumer fileProcessor) { + @NotNull final Project project, + @NotNull final Consumer 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(); }