@VisibleForTesting
  public boolean processChanges(@NotNull VfsEventProcessor eventProcessor) {
    if (!myChangeInfos.isEmpty()) {
      Throwable interruptReason = null;

      try {
        int[] fileIds = myChangeInfos.keys(); // snapshot of the keys
        for (int fileId : fileIds) {
          ProgressManager.checkCanceled();
          ChangeInfo info = myChangeInfos.remove(fileId);
          if (info == null) continue;

          try {
            if (LOG != null) {
              LOG.info("Processing " + info);
            }
            if (!eventProcessor.process(info)) {
              eventProcessor.endBatch();
              return false;
            }
          }
          catch (ProcessCanceledException pce) { // todo remove
            ((FileBasedIndexEx)FileBasedIndex.getInstance()).getLogger().error(pce);
            assert false;
          }
        }
        eventProcessor.endBatch();
      }
      catch (Throwable t) {
        interruptReason = t;
        throw t;
      }
      finally {
        if (LOG != null) {
          LOG.info("Processing " + (interruptReason != null ? "interrupted: " + interruptReason : "finished"));
        }
      }
    }
    return true;
  }
@VisibleForTesting
  public boolean processChanges(@NotNull VfsEventProcessor eventProcessor)