All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.spf4j.concurrent.ContextPropagatingCompletableFuture Maven / Gradle / Ivy

Go to download

A continuously growing collection of utilities to measure performance, get better diagnostics, improve performance, or do things more reliably, faster that other open source libraries...

There is a newer version: 8.10.0
Show newest version
package org.spf4j.concurrent;

import com.google.common.annotations.Beta;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.spf4j.base.ExecutionContext;
import org.spf4j.base.ExecutionContexts;
import org.spf4j.base.TimeSource;
import org.spf4j.base.Timing;
import org.spf4j.base.UncheckedTimeoutException;

/**
 * A Completable future that will wrap all Functions to properly propagate ExecutionContext.
 * see CompletableFuture javadoc for more detail.
 *
 * This works properly only in JDK 11.
 *
 * @author Zoltan Farkas
 */
@SuppressWarnings("checkstyle:DesignForExtension")
@Beta
@ParametersAreNonnullByDefault
public class ContextPropagatingCompletableFuture
        extends InterruptibleCompletableFuture {

  private final ExecutionContext parentContext;

  private final long deadlinenanos;

  @SuppressFBWarnings("EI_EXPOSE_REP2")
  public ContextPropagatingCompletableFuture(final ExecutionContext parentContext, final long deadlinenanos) {
    this.parentContext = parentContext;
    this.deadlinenanos = deadlinenanos;
  }



  @Override
  public  CompletableFuture thenApply(final Function fn) {
    return super.thenApply(ExecutionContexts.propagatingFunction(fn, parentContext,
            null, deadlinenanos));
  }

  @Override
  public  CompletableFuture thenApplyAsync(final Function fn) {
    return super.thenApplyAsync(ExecutionContexts.propagatingFunction(fn, parentContext,
            null, deadlinenanos));
  }

  @Override
  public  CompletableFuture thenApplyAsync(final Function fn, final Executor executor) {
    return super.thenApplyAsync(ExecutionContexts.propagatingFunction(fn, parentContext,
            null, deadlinenanos), executor);
  }

  @Override
  public CompletableFuture thenAccept(final Consumer action) {
    return super.thenAccept(ExecutionContexts.propagatingConsumer(action,
            parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture thenAcceptAsync(final Consumer action) {
    return super.thenAcceptAsync(ExecutionContexts.propagatingConsumer(action,
            parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture thenAcceptAsync(final Consumer action, final Executor executor) {
    return super.thenAcceptAsync(ExecutionContexts.propagatingConsumer(action,
            parentContext, null, deadlinenanos), executor);
  }

  @Override
  public CompletableFuture thenRun(final Runnable action) {
    return super.thenRun(ExecutionContexts.propagatingRunnable(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture thenRunAsync(final Runnable action) {
    return super.thenRunAsync(
            ExecutionContexts.propagatingRunnable(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture thenRunAsync(final Runnable action, final Executor executor) {
    return super.thenRunAsync(
                    ExecutionContexts.propagatingRunnable(action, parentContext, null, deadlinenanos), executor);
  }

  @Override
  public  CompletableFuture thenCombine(final CompletionStage other,
          final BiFunction fn) {
    return super.thenCombine(other,
            ExecutionContexts.propagatingBiFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public  CompletableFuture thenCombineAsync(final CompletionStage other,
          final BiFunction fn) {
    return super.thenCombineAsync(other,
            ExecutionContexts.propagatingBiFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public  CompletableFuture thenCombineAsync(final CompletionStage other,
          final BiFunction fn, final Executor executor) {
    return super.thenCombineAsync(other,
                    ExecutionContexts.propagatingBiFunction(fn, parentContext, null, deadlinenanos),
                    executor);
  }

  @Override
  public  CompletableFuture thenAcceptBoth(final CompletionStage other,
          final BiConsumer action) {
    return super.thenAcceptBoth(other, ExecutionContexts.propagatingBiConsumer(action, parentContext,
                    null, deadlinenanos));
  }

  @Override
  public  CompletableFuture thenAcceptBothAsync(final CompletionStage other,
          final BiConsumer action) {
    return super.thenAcceptBothAsync(other,
            ExecutionContexts.propagatingBiConsumer(action, parentContext,
                    null, deadlinenanos));
  }

  @Override
  public  CompletableFuture thenAcceptBothAsync(final CompletionStage other,
          final BiConsumer action, final Executor executor) {
    return super.thenAcceptBothAsync(other,
            ExecutionContexts.propagatingBiConsumer(action, parentContext,
                    null, deadlinenanos), executor);
  }

  @Override
  public CompletableFuture runAfterBoth(final CompletionStage other, final Runnable action) {
    return super.runAfterBoth(other,
            ExecutionContexts.propagatingRunnable(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture runAfterBothAsync(final CompletionStage other, final Runnable action) {
    return super.runAfterBothAsync(other, ExecutionContexts.propagatingRunnable(action, parentContext,
            null, deadlinenanos));
  }

  @Override
  public CompletableFuture runAfterBothAsync(final CompletionStage other,
          final Runnable action, final Executor executor) {
    return super.runAfterBothAsync(other, ExecutionContexts.propagatingRunnable(action, parentContext,
            null, deadlinenanos), executor);
  }

  @Override
  public  CompletableFuture applyToEither(final CompletionStage other,
          final Function fn) {
    return super.applyToEither(other,
                    ExecutionContexts.propagatingFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public  CompletableFuture applyToEitherAsync(final CompletionStage other,
          final Function fn) {
    return super.applyToEitherAsync(other,
            ExecutionContexts.propagatingFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public  CompletableFuture applyToEitherAsync(final CompletionStage other,
          final Function fn, final Executor executor) {
    return super.applyToEitherAsync(other,
            ExecutionContexts.propagatingFunction(fn, parentContext, null, deadlinenanos), executor);
  }

  @Override
  public CompletableFuture acceptEither(final CompletionStage other,
          final Consumer action) {
    return super.acceptEither(other,
            ExecutionContexts.propagatingConsumer(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture acceptEitherAsync(final CompletionStage other,
          final Consumer action) {
    return super.acceptEitherAsync(other,
            ExecutionContexts.propagatingConsumer(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture acceptEitherAsync(final CompletionStage other,
          final Consumer action, final Executor executor) {
    return super.acceptEitherAsync(other,
            ExecutionContexts.propagatingConsumer(action, parentContext, null, deadlinenanos), executor);
  }

  @Override
  public CompletableFuture runAfterEither(final CompletionStage other, final Runnable action) {
    return super.runAfterEither(other,
            ExecutionContexts.propagatingRunnable(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture runAfterEitherAsync(final CompletionStage other, final Runnable action) {
    return super.runAfterEitherAsync(other,
            ExecutionContexts.propagatingRunnable(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture runAfterEitherAsync(final CompletionStage other,
          final Runnable action, final Executor executor) {
    return super.runAfterEitherAsync(other,
            ExecutionContexts.propagatingRunnable(action, parentContext, null, deadlinenanos), executor);
  }

  @Override
  public  CompletableFuture thenCompose(final Function> fn) {
    return super.thenCompose(ExecutionContexts.propagatingFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public  CompletableFuture thenComposeAsync(final Function> fn) {
    return super.thenComposeAsync(ExecutionContexts.propagatingFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public  CompletableFuture thenComposeAsync(final Function> fn,
          final Executor executor) {
    return super.thenComposeAsync(
            ExecutionContexts.propagatingFunction(fn, parentContext, null, deadlinenanos), executor);
  }

  @Override
  public CompletableFuture exceptionally(final Function fn) {
    return super.exceptionally(ExecutionContexts.propagatingFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture whenComplete(final BiConsumer action) {
    return super.whenComplete(ExecutionContexts.propagatingBiConsumer(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture whenCompleteAsync(final BiConsumer action) {
    return super.whenCompleteAsync(
            ExecutionContexts.propagatingBiConsumer(action, parentContext, null, deadlinenanos));
  }

  @Override
  public CompletableFuture whenCompleteAsync(final BiConsumer action,
          final Executor executor) {
    return super.whenCompleteAsync(
            ExecutionContexts.propagatingBiConsumer(action, parentContext, null, deadlinenanos),
            executor);
  }

  @Override
  public  CompletableFuture handle(final BiFunction fn) {
    return super.handle(ExecutionContexts.propagatingBiFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public  CompletableFuture handleAsync(final BiFunction fn) {
    return super.handleAsync(
            ExecutionContexts.propagatingBiFunction(fn, parentContext, null, deadlinenanos));
  }

  @Override
  public  CompletableFuture handleAsync(final BiFunction fn,
          final Executor executor) {
    return super.handleAsync(
            ExecutionContexts.propagatingBiFunction(fn, parentContext, null, deadlinenanos), executor);
  }

  @Override
  public CompletableFuture toCompletableFuture() {
    return this;
  }

  @Override
  public String toString() {
    return "ContextPropagatingCompletableFuture{" + "parentContext=" + parentContext
            + ", deadlinenanos=" + deadlinenanos + ", super=" + super.toString() +  '}';
  }

  @Override
  @Nullable
  public T get() throws InterruptedException, ExecutionException {
    try {
      long timeout = deadlinenanos - TimeSource.nanoTime();
      if (timeout < 0) {
        throw new UncheckedTimeoutException("deadline exceeded " + Timing.getCurrentTiming()
                .fromNanoTimeToInstant(deadlinenanos));
      }
      return super.get(timeout, TimeUnit.NANOSECONDS);
    } catch (TimeoutException ex) {
      throw new UncheckedTimeoutException(ex);
    }
  }


  /**
   * JDK 11.
   */
  public CompletableFuture completeAsync(final Supplier supplier) {
    try {
      return (CompletableFuture)
              this.getClass().getMethod("completeAsync", new Class[] {Supplier.class})
                .invoke(this, ExecutionContexts.propagatingSupplier(supplier, parentContext, null, deadlinenanos));
    } catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException ex) {
      throw new UnsupportedOperationException("Supported only on JDK 11", ex);
    }
  }

  /**
   * JDK 11.
   */
  public CompletableFuture completeAsync(final Supplier supplier, final Executor executor) {
    try {
      return (CompletableFuture)
              this.getClass().getMethod("completeAsync", new Class[] {Supplier.class, Executor.class})
                      .invoke(this,
                              ExecutionContexts.propagatingSupplier(supplier, parentContext, null, deadlinenanos),
                              executor);
    } catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException ex) {
      throw new UnsupportedOperationException("Supported only on JDK 11", ex);
    }
  }

  /**
   * JDK 11!
   */
  public  CompletableFuture newIncompleteFuture() {
    return new ContextPropagatingCompletableFuture<>(parentContext, deadlinenanos);
  }

  public static  CompletableFuture supplyAsync(final Supplier f) {
    return supplyAsync(f, DefaultExecutor.INSTANCE);
  }

  public static  CompletableFuture supplyAsync(final Supplier f, final Executor e) {
    ExecutionContext current = ExecutionContexts.current();
    if (current == null) {
      return CompletableFuture.supplyAsync(f, e);
    }
    return supplyAsync(f, current, e, current.getDeadlineNanos());
  }

  public static  CompletableFuture supplyAsync(
          final Supplier f, final Executor e, final long deadlineNanos) {
    ExecutionContext current = ExecutionContexts.current();
    if (current == null) {
      return CompletableFuture.supplyAsync(f, e);
    }
    return supplyAsync(f, current, e, deadlineNanos);
  }

  public static  CompletableFuture supplyAsync(final Supplier f,
          final ExecutionContext current,
          final Executor e, final long deadlineNanos) {
    long ctxDeadlineNanos = current.getDeadlineNanos();
    long currentTime = TimeSource.nanoTime();
    long effectiveDeadlineNanos;
    if ((ctxDeadlineNanos - currentTime) > (deadlineNanos - currentTime)) {
      effectiveDeadlineNanos = deadlineNanos;
    } else {
      effectiveDeadlineNanos = ctxDeadlineNanos;
    }
    ContextPropagatingCompletableFuture d
            = new ContextPropagatingCompletableFuture(current, effectiveDeadlineNanos);
    e.execute(() -> {
      try {
        U r;
        try (ExecutionContext ec = ExecutionContexts.start(f.toString(), current,
                currentTime, effectiveDeadlineNanos)) {
          r = f.get();
        }
        d.complete(r);
      } catch (Throwable t) {
        d.completeExceptionally(t);
      }
    });
    return d;
  }

  public static  CompletableFuture completedFuture(final U value) {
    ExecutionContext current = ExecutionContexts.current();
    if (current == null) {
      return CompletableFuture.completedFuture(value);
    } else {
      return completedFuture(current, current.getDeadlineNanos(), value);
    }
  }

  public static  CompletableFuture completedFuture(
          final ExecutionContext parentContext, final long deadlinenanos, final U value) {
    ContextPropagatingCompletableFuture r = new ContextPropagatingCompletableFuture(parentContext, deadlinenanos);
    if (!r.complete(value)) {
      throw new IllegalStateException("Cannot be already completed " + r);
    }
    return r;
  }


}