@Override
  public void run() {
    threadFinished = false;
    while (!isDisposed) {
      try {
        boolean success = false;
        Document document = null;
        Project project = null;
        ProgressIndicator indicator = null;
        try {
          CommitTask task;
          synchronized (documentsToCommit) {
            if (!myEnabled || documentsToCommit.isEmpty()) {
              documentsToCommit.wait();
              continue;
            }
            task = documentsToCommit.pullFirst();
            document = task.document;
            indicator = task.indicator;
            project = task.project;

            log("Pulled", document, false, indicator);

            CommitStage commitStage = getCommitStage(document);
            Document[] uncommitted = null;
            if (commitStage != CommitStage.QUEUED_TO_COMMIT
                || project.isDisposed()
                || !ArrayUtil.contains(
                    document,
                    uncommitted =
                        PsiDocumentManager.getInstance(project).getUncommittedDocuments())) {
              List<Document> documents = uncommitted == null ? null : Arrays.asList(uncommitted);
              log("Abandon and proceeding to next", document, false, commitStage, documents);
              continue;
            }
            if (indicator.isRunning()) {
              useIndicator(indicator);
              document.putUserData(COMMIT_PROGRESS, indicator);
            } else {
              success = true; // document has been marked as removed, e.g. by synchronous commit
            }
          }

          Runnable finishRunnable = null;
          if (!success && !indicator.isCanceled()) {
            try {
              finishRunnable = commit(document, project, null, indicator, false, task.reason);
              success = finishRunnable != null;
              log("DCT.commit returned", document, false, finishRunnable, indicator);
            } finally {
              document.putUserData(COMMIT_PROGRESS, null);
            }
          }

          synchronized (documentsToCommit) {
            if (indicator.isCanceled()) {
              success = false;
            }
            if (success) {
              assert !ApplicationManager.getApplication().isDispatchThread();
              UIUtil.invokeLaterIfNeeded(finishRunnable);
              log(
                  "Invoked later finishRunnable",
                  document,
                  false,
                  success,
                  finishRunnable,
                  indicator);
            }
          }
        } catch (ProcessCanceledException e) {
          cancel(e); // leave queue unchanged
          log("PCE", document, false, e);
          success = false;
        } catch (InterruptedException e) {
          // app must be closing
          int i = 0;
          log("IE", document, false, e);
          cancel(e);
        } catch (Throwable e) {
          LOG.error(e);
          cancel(e);
        }
        synchronized (documentsToCommit) {
          if (!success && indicator.isRunning()) { // running means sync commit has not intervened
            // reset status for queue back successfully
            changeCommitStage(
                document, CommitStage.WAITING_FOR_PSI_APPLY, CommitStage.QUEUED_TO_COMMIT, false);
            changeCommitStage(document, CommitStage.COMMITTED, CommitStage.QUEUED_TO_COMMIT, false);
            doQueue(document, project, CommitStage.QUEUED_TO_COMMIT, "re-added on failure");
          }
        }
      } catch (Throwable e) {
        e.printStackTrace();
        // LOG.error(e);
      }
    }
    threadFinished = true;
    // ping the thread waiting for close
    wakeUpQueue();
    log("Good bye", null, false);
  }
  private void cancel(@NonNls Object reason) {
    log("Canceled", null, false, myProgressIndicator, "Reason: ", reason);

    useIndicator(null);
  }