public @NotNull CompletableFuture<ProgressResult<R>> submit() {
    /*
    General flow:
    1. Create Progress
    2. (opt) Get on write thread to grab modality
    3. Grab modality
    4. (opt) Release IW
    5. Run/Launch task
    6. (opt) Poll tasks on WT
    */

    boolean forceSyncExec = checkIfForceDirectExecNeeded();

    CompletableFuture<? extends @NotNull ProgressIndicator> progressFuture = myProgressIndicatorFuture.thenApply(progress -> {
      // in case of abrupt application exit when 'ProgressManager.getInstance().runProcess(process, progress)' below
      // does not have a chance to run, and as a result the progress won't be disposed
      if (progress instanceof Disposable) {
        Disposer.register(ApplicationManager.getApplication(), (Disposable)progress);
      }
      return progress;
    });

    CompletableFuture<R> resultFuture;
    if (forceSyncExec) {
      resultFuture = new CompletableFuture<>();
      try {
        ProgressIndicator progressIndicator = progressFuture.join();
        resultFuture.complete(ProgressManager.getInstance().runProcess(() -> myComputation.apply(progressIndicator), progressIndicator));
      }
      catch (Throwable t) {
        resultFuture.completeExceptionally(t);
      }
    }
    else {
      Semaphore modalityEntered = new Semaphore(1);
      Function<@NotNull ProgressIndicator, R> onThreadCallable = (progressIndicator) -> {
        if (isModal) {
          modalityEntered.waitFor();
        }
        // runProcess handles starting/stopping progress and setting thread's current progress
        return ProgressManager.getInstance().runProcess(() -> myComputation.apply(progressIndicator), progressIndicator);
      };
      if (ApplicationManager.getApplication().isDispatchThread()) {
        resultFuture = execFromEDT(progressFuture, modalityEntered, onThreadCallable);
      }
      else {
        resultFuture = normalExec(progressFuture, modalityEntered, onThreadCallable);
      }
    }

    return resultFuture.handle((result, e) -> {
      Throwable throwable = unwrap(e);
      if (LOG.isDebugEnabled()) {
        if (throwable != null) {
          LOG.debug("ProgressRunner: task completed with throwable", throwable);
        }

        if (isCanceled(progressFuture)) {
          LOG.debug("ProgressRunner: task cancelled");
        }
      }
      return new ProgressResult<>(result, throwable instanceof ProcessCanceledException || isCanceled(progressFuture), throwable);
    });
  }
public @NotNull CompletableFuture<ProgressResult<R>> submit()