From 2d80bd40155847824dd4a7cd81e07e02b0d34add Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 5 Nov 2014 14:41:37 +0100 Subject: [PATCH] call ReadTask.onCanceled on EDT, only once and only on cancellations caused by write actions --- .../progress/util/ProgressIndicatorUtils.java | 27 ++++++---- .../openapi/progress/util/ReadTask.java | 2 +- .../intellij/ui/EditorNotificationsImpl.java | 11 ++-- .../mvc/MvcModuleStructureSynchronizer.java | 53 +++++++++---------- 4 files changed, 44 insertions(+), 49 deletions(-) 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 368200492cae..05f614637b0a 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 @@ -22,13 +22,18 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; -import com.intellij.ui.AppUIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.ide.PooledThreadExecutor; import java.util.concurrent.Executor; /** + * Methods in this class are used to equip long background processes which take read actions with a special listener + * that fires when a write action is about to begin, and cancels corresponding progress indicators to avoid blocking the UI. + * These processes should be ready to get {@link com.intellij.openapi.progress.ProcessCanceledException} at any moment. + * Processes may want to react on cancellation event by restarting the activity, see + * {@link com.intellij.openapi.progress.util.ReadTask#onCanceled(com.intellij.openapi.progress.ProgressIndicator)} for that. + * * @author gregsh */ public class ProgressIndicatorUtils { @@ -59,15 +64,22 @@ public class ProgressIndicatorUtils { public static void scheduleWithWriteActionPriority(@NotNull final ProgressIndicator progressIndicator, @NotNull final Executor executor, @NotNull final ReadTask readTask) { - AppUIUtil.invokeOnEdt(new Runnable() { + final Application application = ApplicationManager.getApplication(); + // later even if on EDT + // to avoid tasks eagerly restarting immediately, allocating many pooled threads + // which get cancelled too soon when a next write action arrives in the same EDT batch + // (can happen when processing multiple VFS events or writing multiple files on save) + application.invokeLater(new Runnable() { @Override public void run() { - final Application application = ApplicationManager.getApplication(); application.assertIsDispatchThread(); final ApplicationAdapter listener = new ApplicationAdapter() { @Override public void beforeWriteActionStart(Object action) { - progressIndicator.cancel(); + if (!progressIndicator.isCanceled()) { + progressIndicator.cancel(); + readTask.onCanceled(progressIndicator); + } } }; application.addApplicationListener(listener); @@ -100,8 +112,6 @@ public class ProgressIndicatorUtils { ProgressManager.getInstance().runProcess(new Runnable() { @Override public void run() { - // This read action can possible last for a long time, we want it to stop immediately on the first write access. - // For this purpose we launch it under empty progress and invoke progressIndicator#cancel on write access to avoid possible write lock delays. try { ApplicationManager.getApplication().runReadAction(new Runnable() { @Override @@ -112,11 +122,6 @@ public class ProgressIndicatorUtils { } catch (ProcessCanceledException ignore) { } - finally { - if (progressIndicator.isCanceled()) { - task.onCanceled(progressIndicator); - } - } } }, progressIndicator); } diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ReadTask.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ReadTask.java index 4f8f7475bb6f..07e3a800df32 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ReadTask.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ReadTask.java @@ -32,7 +32,7 @@ public interface ReadTask { void computeInReadAction(@NotNull ProgressIndicator indicator); /** - * Is invoked on the background computation thread whenever the computation is canceled by a write action. + * Is invoked on Swing thread whenever the computation is canceled by a write action. * A likely implementation is to restart the computation, maybe based on the new state of the system. */ void onCanceled(@NotNull ProgressIndicator indicator); diff --git a/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java b/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java index 4b788c80ae54..efdbf8edcaaf 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java @@ -160,14 +160,9 @@ public class EditorNotificationsImpl extends EditorNotifications { @Override public void onCanceled(@NotNull ProgressIndicator ignored) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - if (getCurrentProgress(file) == indicator) { - updateNotifications(file); - } - } - }); + if (getCurrentProgress(file) == indicator) { + updateNotifications(file); + } } }; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java index 9af339c55aaa..c314c1bc7de4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcModuleStructureSynchronizer.java @@ -270,42 +270,37 @@ public class MvcModuleStructureSynchronizer extends AbstractProjectComponent { } return; } - - app.invokeLater(new Runnable() { + + final Set> orderSnapshot = takeOrderSnapshot(); + ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ReadTask() { @Override - public void run() { - final Set> orderSnapshot = takeOrderSnapshot(); - ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ReadTask() { + public void computeInReadAction(@NotNull ProgressIndicator indicator) { + if (!isUpToDate()) { + indicator.cancel(); + return; + } + + final Set> actions = computeRawActions(orderSnapshot); + app.invokeLater(new Runnable() { @Override - public void computeInReadAction(@NotNull ProgressIndicator indicator) { + public void run() { if (!isUpToDate()) { - indicator.cancel(); - return; + scheduleRunActions(); + } + else { + runActions(actions); } - - final Set> actions = computeRawActions(orderSnapshot); - app.invokeLater(new Runnable() { - @Override - public void run() { - if (!isUpToDate()) { - scheduleRunActions(); - } - else { - runActions(actions); - } - } - }, ModalityState.NON_MODAL); } + }, ModalityState.NON_MODAL); + } - @Override - public void onCanceled(@NotNull ProgressIndicator indicator) { - scheduleRunActions(); - } + @Override + public void onCanceled(@NotNull ProgressIndicator indicator) { + scheduleRunActions(); + } - private boolean isUpToDate() { - return !myProject.isDisposed() && orderSnapshot.equals(takeOrderSnapshot()); - } - }); + private boolean isUpToDate() { + return !myProject.isDisposed() && orderSnapshot.equals(takeOrderSnapshot()); } }); }