public void processFilesToUpdateInReadAction() {
    processFilesInReadAction(new VfsEventsMerger.VfsEventProcessor() {
      private final StubIndexImpl.FileUpdateProcessor perFileElementTypeUpdateProcessor =
        ((StubIndexImpl)StubIndex.getInstance()).getPerFileElementTypeModificationTrackerUpdateProcessor();
      @Override
      public boolean process(VfsEventsMerger.@NotNull ChangeInfo info) {
        LOG.debug("Processing ", info);
        try {
          int fileId = info.getFileId();
          VirtualFile file = info.getFile();
          List<Project> dirtyQueueProjects = myDirtyFiles.getProjects(info.getFileId());
          if (info.isTransientStateChanged()) myFileBasedIndex.doTransientStateChangeForFile(fileId, file, dirtyQueueProjects);
          if (info.isContentChanged()) myFileBasedIndex.scheduleFileForIndexing(fileId, file, true, dirtyQueueProjects);
          if (info.isFileRemoved()) myFileBasedIndex.doInvalidateIndicesForFile(fileId, file, Collections.emptySet(), dirtyQueueProjects);
          if (info.isFileAdded()) myFileBasedIndex.scheduleFileForIndexing(fileId, file, false, dirtyQueueProjects);
          if (StubIndexImpl.PER_FILE_ELEMENT_TYPE_STUB_CHANGE_TRACKING_SOURCE ==
              StubIndexImpl.PerFileElementTypeStubChangeTrackingSource.ChangedFilesCollector) {
            perFileElementTypeUpdateProcessor.processUpdate(file);
          }
        }
        catch (Throwable t) {
          if (LOG.isDebugEnabled()) LOG.debug("Exception while processing " + info, t);
          throw t;
        }
        finally {
          myDirtyFiles.removeFile(info.getFileId());
        }
        return true;
      }

      @Override
      public void endBatch() {
        if (StubIndexImpl.PER_FILE_ELEMENT_TYPE_STUB_CHANGE_TRACKING_SOURCE ==
            StubIndexImpl.PerFileElementTypeStubChangeTrackingSource.ChangedFilesCollector) {
          perFileElementTypeUpdateProcessor.endUpdatesBatch();
        }
      }
    });
  }
public void processFilesToUpdateInReadAction()