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;
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) {
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);
}
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;
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();
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 {
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);
}
}
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;
}
}
- @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();
}, 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));
}
}
}
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) {
}
finally {
shutdownTracker.unregisterStopperThread(self);
- token.finish();
}
}
}
}
- private enum State { SMART, RUNNING_DUMB_TASKS, WAITING_FOR_FINISH }
+ private enum State { SMART, SCHEDULED_TASKS, RUNNING_DUMB_TASKS, WAITING_FOR_FINISH }
}