run modal indexing when requested explicitly and not during VFS event processing...
authorpeter <peter@jetbrains.com>
Mon, 14 Nov 2016 19:23:19 +0000 (20:23 +0100)
committerpeter <peter@jetbrains.com>
Mon, 14 Nov 2016 19:32:34 +0000 (20:32 +0100)
platform/core-api/src/com/intellij/openapi/project/DumbService.java
platform/core-impl/src/com/intellij/mock/MockDumbService.java
platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java

index bfaef2ab5ce6622b4012b8537c001b51261559a2..180d48ad24a2730cae661daa24ca44be03a5a7b8 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.ModificationTracker;
 import com.intellij.openapi.util.NotNullLazyKey;
@@ -208,10 +209,29 @@ public abstract class DumbService {
     return new ArrayList<T>(collection);
   }
 
+  /**
+   * Queues a task to be executed in "dumb mode", where access to indices is forbidden. Tasks are executed sequentially
+   * in background unless {@link #completeJustSubmittedTasks()} is called in the same dispatch thread activity.<p/>
+   *
+   * Tasks can specify custom "equality" policy via their constructor. Calling this method has no effect if an "equal" task is already enqueued (but not yet running).
+   */
   public abstract void queueTask(@NotNull DumbModeTask task);
-  
+
+  /**
+   * Cancels the given task. If it's in the queue, it won't be executed. If it's already running, its {@link com.intellij.openapi.progress.ProgressIndicator} is canceled, so the next {@link ProgressManager#checkCanceled()} call
+   * will throw {@link com.intellij.openapi.progress.ProcessCanceledException}.
+   */
   public abstract void cancelTask(@NotNull DumbModeTask task);
 
+  /**
+   * Runs the "just submitted" tasks under a modal dialog. "Just submitted" means that tasks were queued for execution
+   * earlier within the same Swing event dispatch thread event processing, and there were no other tasks already running at that moment. Otherwise this method does nothing.<p/>
+   *
+   * This functionality can be useful in refactorings (invoked in "smart mode"), when after VFS or root changes
+   * (which could start "dumb mode") some reference resolve is required (which again requires "smart mode").
+   */
+  public abstract void completeJustSubmittedTasks();
+
   public abstract JComponent wrapGently(@NotNull JComponent dumbUnawareContent, @NotNull Disposable parentDisposable);
 
   public void makeDumbAware(@NotNull final JComponent component, @NotNull Disposable disposable) {
@@ -279,17 +299,10 @@ public abstract class DumbService {
   public abstract boolean isAlternativeResolveEnabled();
 
   /**
-   * By default, dumb mode tasks (including indexing) are allowed in non-modal state only. The reason is that
-   * when some code shows a dialog, it probably does't expect that after the dialog is closed the dumb mode will be on.
-   * Therefore any dumb mode started within a dialog is considered a mistake, performed under modal progress and reported as an exception.<p/>
-   *
-   * If the dialog (e.g. Project Structure) starting background dumb mode is an expected situation, the dumb mode should be started inside the runnable
-   * passed to this method. This will suppress the exception and allow either modal or background indexing. Note that this will only affect the invocation time
-   * modality state, so showing other dialogs from within the runnable and starting dumb mode from them would still result in an assertion failure.<p/>
-   *
-   * If this exception occurs inside invokeLater call which happens to run when a modal dialog is shown, the correct fix is supplying an explicit modality state
-   * in {@link com.intellij.openapi.application.Application#invokeLater(Runnable, ModalityState)}.
+   * Obsolete, does nothing, just executes the passed runnable.
+   * @see #completeJustSubmittedTasks()
    */
+  @Deprecated
   public static void allowStartingDumbModeInside(@NotNull DumbModePermission permission, @NotNull Runnable runnable) {
     ServiceManager.getService(DumbPermissionService.class).allowStartingDumbModeInside(permission, runnable);
   }
index e97a9f64c7f3263788ede4e4df0681c5e7514f3c..cf06dfb0ca17060f3904cc04b0c454e8dc6ce2a6 100644 (file)
@@ -66,6 +66,10 @@ public class MockDumbService extends DumbService {
   @Override
   public void cancelTask(@NotNull DumbModeTask task) { }
 
+  @Override
+  public void completeJustSubmittedTasks() {
+  }
+
   @Override
   public JComponent wrapGently(@NotNull JComponent dumbUnawareContent, @NotNull Disposable parentDisposable) {
     throw new UnsupportedOperationException();
index 0e8c5c4348c77565d3e00d0554bee61bd6306268..950bed627ce3b713a09d85a21df334381ce9df8a 100644 (file)
@@ -16,7 +16,6 @@
 package com.intellij.openapi.project;
 
 import com.intellij.ide.IdeBundle;
-import com.intellij.ide.startup.StartupManagerEx;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.application.*;
 import com.intellij.openapi.application.impl.ApplicationImpl;
@@ -166,7 +165,6 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
     TransactionId contextTransaction = TransactionGuard.getInstance().getContextTransaction();
 
     final Throwable trace = ourForcedTrace != null ? ourForcedTrace : new Throwable(); // please report exceptions here to peter
-    final DumbModePermission schedulerPermission = getExplicitPermission();
     if (LOG.isDebugEnabled()) LOG.debug("Scheduling task " + task);
     final Application application = ApplicationManager.getApplication();
 
@@ -189,7 +187,7 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
       return;
     }
 
-    Runnable runnable = () -> queueTaskOnEdt(task, contextTransaction, trace, schedulerPermission);
+    Runnable runnable = () -> queueTaskOnEdt(task, contextTransaction, trace);
     if (application.isDispatchThread()) {
       runnable.run(); // will log errors if not already in a write-safe context
     } else {
@@ -199,19 +197,12 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
 
   private void queueTaskOnEdt(@NotNull DumbModeTask task,
                               @Nullable TransactionId contextTransaction,
-                              @NotNull Throwable trace,
-                              @Nullable DumbModePermission schedulerPermission) {
+                              @NotNull Throwable trace) {
     if (!addTaskToQueue(task)) return;
 
-    if (myState.get() != State.RUNNING_DUMB_TASKS) {
+    if (myState.get() == State.SMART || myState.get() == State.WAITING_FOR_FINISH) {
       WriteAction.run(() -> enterDumbMode(contextTransaction, trace));
-
-      if (shouldStartModalProgress(schedulerPermission, trace)) {
-        showModalProgress();
-      }
-      else {
-        startBackgroundProcess();
-      }
+      ApplicationManager.getApplication().invokeLater(this::startBackgroundProcess);
     }
   }
 
@@ -230,21 +221,10 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
     return true;
   }
 
-  private boolean shouldStartModalProgress(@Nullable DumbModePermission schedulerPermission, @NotNull Throwable trace) {
-    DumbModePermission permission = schedulerPermission != null ? schedulerPermission : getEdtPermission();
-    if (permission == null) {
-      LOG.info("Dumb mode not permitted in modal environment; see DumbService.allowStartingDumbModeInside documentation", trace);
-    }
-    else if (permission == DumbModePermission.MAY_START_MODAL) {
-      LOG.debug("Starting modal dumb mode, caused by the following trace", trace);
-    }
-    return permission != DumbModePermission.MAY_START_BACKGROUND;
-  }
-
   private void enterDumbMode(@Nullable TransactionId contextTransaction, @NotNull Throwable trace) {
     boolean wasSmart = !isDumb();
     synchronized (myRunWhenSmartQueue) {
-      myState.set(State.RUNNING_DUMB_TASKS);
+      myState.set(State.SCHEDULED_TASKS);
     }
     myDumbStart = trace;
     myDumbStartTransaction = contextTransaction;
@@ -259,20 +239,6 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
     }
   }
 
-  @Nullable
-  private DumbModePermission getEdtPermission() {
-    DumbModePermission permission = getExplicitPermission();
-    if (permission != null) {
-      return permission;
-    }
-
-    if (ModalityState.current() == ModalityState.NON_MODAL || !StartupManagerEx.getInstanceEx(myProject).postStartupActivityPassed()) {
-      return DumbModePermission.MAY_START_BACKGROUND;
-    }
-
-    return null;
-  }
-
   @Nullable
   public static DumbModePermission getExplicitPermission() {
     return ourPermissionService.getValue().getPermission();
@@ -417,13 +383,26 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
     }, modalityState, myProject.getDisposed());
   }
 
+  @Override
+  public void completeJustSubmittedTasks() {
+    assert myProject.isInitialized();
+    if (myState.get() != State.SCHEDULED_TASKS) {
+      return;
+    }
+
+    while (isDumb()) {
+      showModalProgress();
+    }
+  }
+
   private void showModalProgress() {
     try {
       ((ApplicationImpl)ApplicationManager.getApplication()).executeSuspendingWriteAction(myProject, IdeBundle.message("progress.indexing"), () ->
         runBackgroundProcess(ProgressManager.getInstance().getProgressIndicator(), true));
     }
     finally {
-      queueUpdateFinished(true);
+      if (myState.get() != State.WAITING_FOR_FINISH) throw new AssertionError(myState.get());
+      WriteAction.run(() -> updateFinished(true));
     }
   }
 
@@ -443,10 +422,11 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
   }
 
   private void runBackgroundProcess(@NotNull final ProgressIndicator visibleIndicator, boolean modal) {
+    if (!myState.compareAndSet(State.SCHEDULED_TASKS, State.RUNNING_DUMB_TASKS)) return;
+
     final ShutDownTracker shutdownTracker = ShutDownTracker.getInstance();
     final Thread self = Thread.currentThread();
-    AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("Performing indexing tasks");
-    try {
+    try (AccessToken ignored = HeavyProcessLatch.INSTANCE.processStarted("Performing indexing tasks")) {
       shutdownTracker.registerStopperThread(self);
 
       if (visibleIndicator instanceof ProgressIndicatorEx) {
@@ -477,7 +457,6 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
     }
     finally {
       shutdownTracker.unregisterStopperThread(self);
-      token.finish();
     }
   }
 
@@ -589,5 +568,5 @@ public class DumbServiceImpl extends DumbService implements Disposable, Modifica
     }
   }
 
-  private enum State { SMART, RUNNING_DUMB_TASKS, WAITING_FOR_FINISH }
+  private enum State { SMART, SCHEDULED_TASKS, RUNNING_DUMB_TASKS, WAITING_FOR_FINISH }
 }