@Override
  public void runWhenProjectIsInitialized(@NotNull final Runnable action) {
    final Application application = ApplicationManager.getApplication();
    if (application == null) return;

    //noinspection SynchronizeOnThis
    synchronized (this) {
      // in tests which simulate project opening, post-startup activities could have been run
      // already.
      // Then we should act as if the project was initialized
      boolean initialized =
          myProject.isInitialized()
              || application.isUnitTestMode() && myPostStartupActivitiesPassed;
      if (!initialized) {
        registerPostStartupActivity(action);
        return;
      }
    }

    Runnable runnable =
        new Runnable() {
          @Override
          public void run() {
            if (!myProject.isDisposed()) {
              action.run();
            }
          }
        };
    if (application.isDispatchThread() && ModalityState.current() == ModalityState.NON_MODAL) {
      runnable.run();
    } else {
      application.invokeLater(runnable, ModalityState.NON_MODAL);
    }
  }
  public void startCacheUpdate() {
    try {
      DumbServiceImpl dumbService = DumbServiceImpl.getInstance(myProject);

      if (!ApplicationManager.getApplication().isUnitTestMode()) {
        // pre-startup activities have registered dumb tasks that load VFS (scanning files to index)
        // only after these tasks pass does VFS refresh make sense
        dumbService.queueTask(
            new DumbModeTask() {
              @Override
              public void performInDumbMode(@NotNull ProgressIndicator indicator) {
                scheduleInitialVfsRefresh();
              }

              @Override
              public String toString() {
                return "initial refresh";
              }
            });
      }
    } catch (ProcessCanceledException e) {
      throw e;
    } catch (Throwable e) {
      LOG.error(e);
    }
  }
  public void runStartupActivities() {
    ApplicationManager.getApplication()
        .runReadAction(
            new Runnable() {
              @Override
              @SuppressWarnings("SynchronizeOnThis")
              public void run() {
                AccessToken token =
                    HeavyProcessLatch.INSTANCE.processStarted("Running Startup Activities");
                try {
                  runActivities(myPreStartupActivities);

                  // to avoid atomicity issues if runWhenProjectIsInitialized() is run at the same
                  // time
                  synchronized (StartupManagerImpl.this) {
                    myPreStartupActivitiesPassed = true;
                    myStartupActivitiesRunning = true;
                  }

                  runActivities(myStartupActivities);

                  synchronized (StartupManagerImpl.this) {
                    myStartupActivitiesRunning = false;
                    myStartupActivitiesPassed = true;
                  }
                } finally {
                  token.finish();
                }
              }
            });
  }
  public void runPostStartupActivities() {
    if (postStartupActivityPassed()) {
      return;
    }

    final Application app = ApplicationManager.getApplication();

    if (!app.isHeadlessEnvironment()) {
      checkFsSanity();
      checkProjectRoots();
    }

    runActivities(myDumbAwarePostStartupActivities);

    DumbService.getInstance(myProject)
        .runWhenSmart(
            new Runnable() {
              @Override
              public void run() {
                app.assertIsDispatchThread();

                // myDumbAwarePostStartupActivities might be non-empty if new activities were
                // registered during dumb mode
                runActivities(myDumbAwarePostStartupActivities);

                //noinspection SynchronizeOnThis
                synchronized (StartupManagerImpl.this) {
                  if (!myNotDumbAwarePostStartupActivities.isEmpty()) {
                    while (!myNotDumbAwarePostStartupActivities.isEmpty()) {
                      queueSmartModeActivity(myNotDumbAwarePostStartupActivities.remove(0));
                    }

                    // return here later to set myPostStartupActivitiesPassed
                    DumbService.getInstance(myProject).runWhenSmart(this);
                  } else {
                    myPostStartupActivitiesPassed = true;
                  }
                }
              }
            });

    // otherwise will be stored - we must not create config files in tests
    if (!app.isUnitTestMode()) {
      Registry.get("ide.firstStartup").setValue(false);
    }
  }