private void startBackgroundProcess(
      @NotNull final DumbModeTask task, @NotNull final ProgressIndicatorEx indicator) {
    ProgressManager.getInstance()
        .run(
            new Task.Backgroundable(myProject, IdeBundle.message("progress.indexing"), false) {

              @Override
              public void run(@NotNull final ProgressIndicator visibleIndicator) {
                if (ApplicationManager.getApplication().isInternal())
                  LOG.info("Running dumb mode task: " + task);

                final ShutDownTracker shutdownTracker = ShutDownTracker.getInstance();
                final Thread self = Thread.currentThread();
                try {
                  HeavyProcessLatch.INSTANCE.processStarted();
                  shutdownTracker.registerStopperThread(self);

                  indicator.checkCanceled();

                  if (visibleIndicator instanceof ProgressIndicatorEx) {
                    indicator.addStateDelegate((ProgressIndicatorEx) visibleIndicator);
                    ((ProgressIndicatorEx) visibleIndicator)
                        .addStateDelegate(new AppIconProgress());
                  }

                  indicator.setIndeterminate(true);
                  indicator.setText(IdeBundle.message("progress.indexing.scanning"));

                  task.performInDumbMode(indicator);
                } catch (ProcessCanceledException ignored) {
                } catch (Throwable unexpected) {
                  LOG.error(unexpected);
                } finally {
                  shutdownTracker.unregisterStopperThread(self);
                  HeavyProcessLatch.INSTANCE.processFinished();
                  taskFinished(task);
                }
              }
            });
  }
  private void scheduleCacheUpdate(@NotNull final DumbModeTask task, boolean forceDumbMode) {
    if (ApplicationManager.getApplication().isInternal()) LOG.info("schedule " + task);
    final Application application = ApplicationManager.getApplication();

    if (application.isUnitTestMode()
        || application.isHeadlessEnvironment()
        || !forceDumbMode && !myDumb && application.isReadAccessAllowed()) {
      final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
      if (indicator != null) {
        indicator.pushState();
      }
      try {
        HeavyProcessLatch.INSTANCE.processStarted();
        task.performInDumbMode(indicator != null ? indicator : new EmptyProgressIndicator());
      } finally {
        HeavyProcessLatch.INSTANCE.processFinished();
        if (indicator != null) {
          indicator.popState();
        }
        Disposer.dispose(task);
      }
      return;
    }

    UIUtil.invokeLaterIfNeeded(
        new DumbAwareRunnable() {
          @Override
          public void run() {
            if (myProject.isDisposed()) {
              return;
            }
            final ProgressIndicatorBase indicator = new ProgressIndicatorBase();
            myProgresses.put(task, indicator);
            Disposer.register(
                task,
                new Disposable() {
                  @Override
                  public void dispose() {
                    application.assertIsDispatchThread();
                    myProgresses.remove(task);
                  }
                });
            // ok to test and set the flag like this, because the change is always done from
            // dispatch thread
            if (!myDumb) {
              // always change dumb status inside write action.
              // This will ensure all active read actions are completed before the app goes dumb
              boolean startSuccess =
                  application.runWriteAction(
                      new Computable<Boolean>() {
                        @Override
                        public Boolean compute() {
                          myDumb = true;
                          try {
                            myPublisher.enteredDumbMode();
                          } catch (Throwable e) {
                            LOG.error(e);
                          }

                          try {
                            startBackgroundProcess(task, indicator);
                          } catch (Throwable e) {
                            LOG.error("Failed to start background index update task", e);
                            return false;
                          }
                          return true;
                        }
                      });
              if (!startSuccess) {
                updateFinished();
              }
            } else {
              myUpdatesQueue.addLast(task);
            }
          }
        });
  }