public void apply(@NotNull VirtualFile file, @Nullable Runnable callback, boolean forceApplicationOnSameThread) {
    this.wasForcedToApplyOnTheSameThread = forceApplicationOnSameThread;
    long startTime = System.nanoTime();
    if (removeDataFromIndicesForFile) {
      myIndex.removeDataFromIndicesForFile(fileId, file, "invalid_or_large_file_or_indexing_delete_request");
    }

    if (appliers.isEmpty() && removers.isEmpty()) {
      doPostModificationJob(file, true, () -> "empty appliers");
      separateApplicationTimeNanos.set(System.nanoTime() - startTime);
      if (callback != null) {
        callback.run();
      }
      return;
    }

    if (getApplicationMode() == ApplicationMode.SameThreadOutsideReadLock) {
      separateApplicationTimeNanos.set(System.nanoTime() - startTime);
      applyModifications(file, -1, null, callback, "same thread");
      return;
    }

    BitSet executorsToSchedule = new BitSet(TOTAL_WRITERS_NUMBER);

    for (SingleIndexValueApplier<?> applier : appliers) {
      executorsToSchedule.set(getExecutorIndex(applier.indexId));
    }

    for (SingleIndexValueRemover remover : removers) {
      executorsToSchedule.set(getExecutorIndex(remover.indexId));
    }

    // Schedule appliers to dedicated executors
    AtomicInteger syncCounter = new AtomicInteger(executorsToSchedule.cardinality());
    separateApplicationTimeNanos.set(0);

    for (int i = 0; i < TOTAL_WRITERS_NUMBER; i++) {
      if (executorsToSchedule.get(i)) {
        var executorIndex = i;
        scheduleIndexWriting(executorIndex,
                             () -> applyModifications(file, executorIndex, syncCounter, callback, "executor " + executorIndex));
      }
    }

    separateApplicationTimeNanos.addAndGet(System.nanoTime() - startTime);
  }
public void apply(@NotNull VirtualFile file, @Nullable Runnable callback, boolean forceApplicationOnSameThread)