code: public <ReturnType> CompletableFuture<ReturnType> add(Supplier<CompletableFuture<? extends ReturnType>> toRun) {
          CompletableFuture<ReturnType> result = new CompletableFuture<>();
          CompletableFuture<?> existingTask;
          synchronized (this.lock) {
              Exceptions.checkNotClosed(this.closed, this);
              existingTask = this.lastTask;
              if (existingTask != null) {
                  // Another task is in progress. Queue up behind it, and make sure to only start the execution once
                  // it is done.
                  existingTask.whenCompleteAsync((r, ex) -> Futures.completeAfter(toRun, result), this.executor);
              }
  
              this.lastTask = result;
          }
  
          if (existingTask == null) {
              // There is no previously running task. Need to trigger its execution now, outside of the synchronized block.
              Futures.completeAfter(toRun, result);
          }
  
          // Cleanup: if no more tasks are registered after this one, then clean up the field.
          result.whenComplete((r, ex) -> {
              synchronized (this.lock) {
                  if (this.lastTask != null && this.lastTask.isDone()) {
                      this.lastTask = null;
                  }
              }
          });
  
          return result;
      }
docs: Queues up a new task to execute.
  
  This task will not begin execution until all previous tasks have finished. In addition, no subsequent task
  will begin executing until this task has finished executing.
  
  @param toRun        A Supplier that will be invoked when it is this task's turn to run. It will return a
  CompletableFuture that will complete when this task completes.
  @param <ReturnType> Return type.
  @return A CompletableFuture that will complete with the result from the CompletableFuture returned by toRun,
  when toRun completes executing.