public boolean closeProject(
      @NotNull final Project project,
      final boolean save,
      final boolean dispose,
      boolean checkCanClose) {
    if (isLight(project)) {
      throw new AssertionError("must not close light project");
    }
    if (!isProjectOpened(project)) return true;
    if (checkCanClose && !canClose(project)) return false;
    final ShutDownTracker shutDownTracker = ShutDownTracker.getInstance();
    shutDownTracker.registerStopperThread(Thread.currentThread());
    try {
      if (save) {
        FileDocumentManager.getInstance().saveAllDocuments();
        project.save();
      }

      if (checkCanClose && !ensureCouldCloseIfUnableToSave(project)) {
        return false;
      }

      fireProjectClosing(project); // somebody can start progress here, do not wrap in write action

      ApplicationManager.getApplication()
          .runWriteAction(
              new Runnable() {
                @Override
                public void run() {
                  synchronized (myOpenProjects) {
                    myOpenProjects.remove(project);
                    cacheOpenProjects();
                    myTestProjects.remove(project);
                  }

                  myChangedProjectFiles.remove(project);

                  fireProjectClosed(project);

                  if (dispose) {
                    Disposer.dispose(project);
                  }
                }
              });
    } finally {
      shutDownTracker.unregisterStopperThread(Thread.currentThread());
    }

    return true;
  }