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

net.tascalate.concurrent.ConfigurableDependentPromise Maven / Gradle / Ivy

Go to download

Implementation of blocking (IO-Bound) cancellable java.util.concurrent.CompletionStage and related extensions to java.util.concurrent.ExecutorService-s

There is a newer version: 0.9.8
Show newest version
/**
 * Copyright 2015-2019 Valery Silaev (http://vsilaev.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.tascalate.concurrent;

import static net.tascalate.concurrent.LinkedCompletion.FutureCompletion;
import static net.tascalate.concurrent.SharedFunctions.cancelPromise;
import static net.tascalate.concurrent.SharedFunctions.selectFirst;

import java.time.Duration;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
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 net.tascalate.concurrent.decorators.AbstractPromiseDecorator; 

/**
 * 
 * 

{@link DependentPromise} implementation, i.e. concrete wrapper that may keep track origin of this promise * and cancel them along with this promise itself. * * For example: *

 * 
 * DependentPromise<?> p1 = DependentPromise.from(CallableTask.runAsync(this::someLongRunningMethod, myExecutor));
 * DependentPromise<?> p2 = p1.thenRunAsync(this::someOtherLongRunningTask, true);
 * ...
 * p2.cancel(true); 
 *  
 * 
 * 
*

In the example p2 is created with specifying p1 as origin (last argument is true). * Now when canceling p2 both p2 and p1 will be cancelled if not completed yet. * *

The class implements overloads to all composition methods declared in {@link DependentPromise} interface. * *

The ones that accepts another {@link CompletionStage} as argument (named *Both* and * *Either* are overloaded with a set of @{link {@link PromiseOrigin} as an argument to let * you specify what to enlist as origin: "this" related to method call or the parameter. * *

Rest of methods from {@link CompletionStage} API are overloaded with boolean argument * enlistOrigin that specify whether or not the {@link Promise} object whose * method is invoiked should be added as an origin to result. * *

All methods originally specified in {@link CompletionStage} does not add "this" as an origin to * resulting promise. * * @author vsilaev * * @param * a type of the successfully resolved promise value */ public class ConfigurableDependentPromise implements DependentPromise { final protected Promise delegate; final protected CompletionStage[] cancellableOrigins; final private Set defaultEnlistOptions; protected ConfigurableDependentPromise(Promise delegate, Set defaultEnlistOptions, CompletionStage[] cancellableOrigins) { this.delegate = delegate; this.defaultEnlistOptions = defaultEnlistOptions == null || defaultEnlistOptions.isEmpty() ? PromiseOrigin.NONE : defaultEnlistOptions; this.cancellableOrigins = cancellableOrigins; } public static DependentPromise from(Promise source) { return from(source, PromiseOrigin.NONE); } public static DependentPromise from(Promise source, Set defaultEnlistOptions) { return doWrap(source, defaultEnlistOptions, null); } protected DependentPromise wrap(Promise original, CompletionStage[] cancellableOrigins) { return doWrap(original, defaultEnlistOptions, cancellableOrigins); } private static DependentPromise doWrap(Promise original, Set defaultEnlistOptions, CompletionStage[] cancellableOrigins) { if (null == cancellableOrigins || cancellableOrigins.length == 0) { // Nothing to enlist additionally for this "original" instance if (original instanceof ConfigurableDependentPromise) { ConfigurableDependentPromise ioriginal = (ConfigurableDependentPromise)original; if (identicalSets(ioriginal.defaultEnlistOptions, defaultEnlistOptions)) { // Same defaultEnlistOptions, may reuse return ioriginal; } } } final ConfigurableDependentPromise result = new ConfigurableDependentPromise<>(original, defaultEnlistOptions, cancellableOrigins); if (result.isCancelled()) { // Wrapped over already cancelled Promise // So result.cancel() has no effect // and we have to cancel origins explicitly // right after construction cancelPromises(result.cancellableOrigins, true); } return result; } // All delay overloads delegate to these methods @Override public DependentPromise delay(Duration duration, boolean delayOnError) { return delay(duration, delayOnError, defaultEnlistOrigin()); } @Override public DependentPromise delay(Duration duration, boolean delayOnError, boolean enlistOrigin) { if (!(delayOnError || enlistOrigin)) { // Fast route return thenCompose( v -> thenCombineAsync(Timeouts.delay(duration), selectFirst(), PromiseOrigin.PARAM_ONLY) ); } CompletableFuture> delayed = new CompletableFuture<>(); whenComplete(Timeouts.configureDelay(this, delayed, duration, delayOnError)); // Use *Async to execute on default "this" executor return this.thenApply(Try::success, enlistOrigin) .exceptionally(Try::failure, true) .thenCombineAsync(delayed, (u, v) -> u.done(), PromiseOrigin.ALL); } // All orTimeout overloads delegate to these methods @Override public DependentPromise orTimeout(Duration duration, boolean cancelOnTimeout) { return orTimeout(duration, cancelOnTimeout, defaultEnlistOrigin()); } @Override public DependentPromise orTimeout(Duration duration, boolean cancelOnTimeout, boolean enlistOrigin) { Promise> onTimeout = Timeouts.delayed(null, duration); DependentPromise result = this.thenApply(Try::success, enlistOrigin) .exceptionally(Try::failure, true) // Use *Async to execute on default "this" executor .applyToEitherAsync(onTimeout, v -> Try.doneOrTimeout(v, duration), PromiseOrigin.ALL); result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout)); return result; } @Override public DependentPromise onTimeout(T value, Duration duration, boolean cancelOnTimeout) { return onTimeout(value, duration, cancelOnTimeout, defaultEnlistOrigin()); } @Override public DependentPromise onTimeout(T value, Duration duration, boolean cancelOnTimeout, boolean enlistOrigin) { Promise> onTimeout = Timeouts.delayed(Try.success(value), duration); DependentPromise result = this.thenApply(Try::success, enlistOrigin) .exceptionally(Try::failure, true) // Use *Async to execute on default "this" executor .applyToEitherAsync(onTimeout, Try::done, PromiseOrigin.ALL); result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout)); return result; } // All onTimeout overloads delegate to this method @Override public DependentPromise onTimeout(Supplier supplier, Duration duration, boolean cancelOnTimeout) { return onTimeout(supplier, duration, cancelOnTimeout, defaultEnlistOrigin()); } @Override public DependentPromise onTimeout(Supplier supplier, Duration duration, boolean cancelOnTimeout, boolean enlistOrigin) { // timeout converted to supplier Promise>> onTimeout = Timeouts.delayed(Try.with(supplier), duration); DependentPromise result = this.thenApply(Try::success, enlistOrigin) .exceptionally(Try::failure, true) .thenApply(SharedFunctions::supply, true) // Use *Async to execute on default "this" executor .applyToEitherAsync(onTimeout, s -> s.get().done(), PromiseOrigin.ALL); result.whenComplete(Timeouts.timeoutsCleanup(this, onTimeout, cancelOnTimeout)); return result; } public DependentPromise thenApply(Function fn, boolean enlistOrigin) { return wrap(delegate.thenApply(fn), origin(enlistOrigin)); } public DependentPromise thenApplyAsync(Function fn, boolean enlistOrigin) { return wrap(delegate.thenApplyAsync(fn), origin(enlistOrigin)); } public DependentPromise thenApplyAsync(Function fn, Executor executor, boolean enlistOrigin) { return wrap(delegate.thenApplyAsync(fn, executor), origin(enlistOrigin)); } public DependentPromise thenAccept(Consumer action, boolean enlistOrigin) { return wrap(delegate.thenAccept(action), origin(enlistOrigin)); } public DependentPromise thenAcceptAsync(Consumer action, boolean enlistOrigin) { return wrap(delegate.thenAcceptAsync(action), origin(enlistOrigin)); } public DependentPromise thenAcceptAsync(Consumer action, Executor executor, boolean enlistOrigin) { return wrap(delegate.thenAcceptAsync(action, executor), origin(enlistOrigin)); } public DependentPromise thenRun(Runnable action, boolean enlistOrigin) { return wrap(delegate.thenRun(action), origin(enlistOrigin)); } public DependentPromise thenRunAsync(Runnable action, boolean enlistOrigin) { return wrap(delegate.thenRunAsync(action), origin(enlistOrigin)); } public DependentPromise thenRunAsync(Runnable action, Executor executor, boolean enlistOrigin) { return wrap(delegate.thenRunAsync(action, executor), origin(enlistOrigin)); } public DependentPromise thenCombine(CompletionStage other, BiFunction fn, Set enlistOptions) { return wrap(delegate.thenCombine(other, fn), originAndParam(other, enlistOptions)); } public DependentPromise thenCombineAsync(CompletionStage other, BiFunction fn, Set enlistOptions) { return wrap(delegate.thenCombineAsync(other, fn), originAndParam(other, enlistOptions)); } public DependentPromise thenCombineAsync(CompletionStage other, BiFunction fn, Executor executor, Set enlistOptions) { return wrap(delegate.thenCombineAsync(other, fn, executor), originAndParam(other, enlistOptions)); } public DependentPromise thenAcceptBoth(CompletionStage other, BiConsumer action, Set enlistOptions) { return wrap(delegate.thenAcceptBoth(other, action), originAndParam(other, enlistOptions)); } public DependentPromise thenAcceptBothAsync(CompletionStage other, BiConsumer action, Set enlistOptions) { return wrap(delegate.thenAcceptBothAsync(other, action), originAndParam(other, enlistOptions)); } public DependentPromise thenAcceptBothAsync(CompletionStage other, BiConsumer action, Executor executor, Set enlistOptions) { return wrap(delegate.thenAcceptBothAsync(other, action, executor), originAndParam(other, enlistOptions)); } public DependentPromise runAfterBoth(CompletionStage other, Runnable action, Set enlistOptions) { return wrap(delegate.runAfterBoth(other, action), originAndParam(other, enlistOptions)); } public DependentPromise runAfterBothAsync(CompletionStage other, Runnable action, Set enlistOptions) { return wrap(delegate.runAfterBothAsync(other, action), originAndParam(other, enlistOptions)); } public DependentPromise runAfterBothAsync(CompletionStage other, Runnable action, Executor executor, Set enlistOptions) { return wrap(delegate.runAfterBothAsync(other, action, executor), originAndParam(other, enlistOptions)); } public DependentPromise applyToEither(CompletionStage other, Function fn, Set enlistOptions) { return wrap(delegate.applyToEither(other, fn), originAndParam(other, enlistOptions)); } public DependentPromise applyToEitherAsync(CompletionStage other, Function fn, Set enlistOptions) { return wrap(delegate.applyToEitherAsync(other, fn), originAndParam(other, enlistOptions)); } public DependentPromise applyToEitherAsync(CompletionStage other, Function fn, Executor executor, Set enlistOptions) { return wrap(delegate.applyToEitherAsync(other, fn, executor), originAndParam(other, enlistOptions)); } public DependentPromise acceptEither(CompletionStage other, Consumer action, Set enlistOptions) { return wrap(delegate.acceptEither(other, action), originAndParam(other, enlistOptions)); } public DependentPromise acceptEitherAsync(CompletionStage other, Consumer action, Set enlistOptions) { return wrap(delegate.acceptEitherAsync(other, action), originAndParam(other, enlistOptions)); } public DependentPromise acceptEitherAsync(CompletionStage other, Consumer action, Executor executor, Set enlistOptions) { return wrap(delegate.acceptEitherAsync(other, action, executor), originAndParam(other, enlistOptions)); } public DependentPromise runAfterEither(CompletionStage other, Runnable action, Set enlistOptions) { return wrap(delegate.runAfterEither(other, action), originAndParam(other, enlistOptions)); } public DependentPromise runAfterEitherAsync(CompletionStage other, Runnable action, Set enlistOptions) { return wrap(delegate.runAfterEitherAsync(other, action), originAndParam(other, enlistOptions)); } public DependentPromise runAfterEitherAsync(CompletionStage other, Runnable action, Executor executor, Set enlistOptions) { return wrap(delegate.runAfterEitherAsync(other, action, executor), originAndParam(other, enlistOptions)); } public DependentPromise thenCompose(Function> fn, boolean enlistOrigin) { return wrap(delegate.thenCompose(fn), origin(enlistOrigin)); } public DependentPromise thenComposeAsync(Function> fn, boolean enlistOrigin) { return wrap(delegate.thenComposeAsync(fn), origin(enlistOrigin)); } public DependentPromise thenComposeAsync(Function> fn, Executor executor, boolean enlistOrigin) { return wrap(delegate.thenComposeAsync(fn, executor), origin(enlistOrigin)); } public DependentPromise exceptionally(Function fn, boolean enlistOrigin) { return wrap(delegate.exceptionally(fn), origin(enlistOrigin)); } public DependentPromise whenComplete(BiConsumer action, boolean enlistOrigin) { return wrap(delegate.whenComplete(action), origin(enlistOrigin)); } public DependentPromise whenCompleteAsync(BiConsumer action, boolean enlistOrigin) { return wrap(delegate.whenCompleteAsync(action), origin(enlistOrigin)); } public DependentPromise whenCompleteAsync(BiConsumer action, Executor executor, boolean enlistOrigin) { return wrap(delegate.whenCompleteAsync(action, executor), origin(enlistOrigin)); } public DependentPromise handle(BiFunction fn, boolean enlistOrigin) { return wrap(delegate.handle(fn), origin(enlistOrigin)); } public DependentPromise handleAsync(BiFunction fn, boolean enlistOrigin) { return wrap(delegate.handleAsync(fn), origin(enlistOrigin)); } public DependentPromise handleAsync(BiFunction fn, Executor executor, boolean enlistOrigin) { return wrap(delegate.handleAsync(fn, executor), origin(enlistOrigin)); } @Override public DependentPromise thenApply(Function fn) { return thenApply(fn, defaultEnlistOrigin()); } @Override public DependentPromise thenApplyAsync(Function fn) { return thenApplyAsync(fn, defaultEnlistOrigin()); } @Override public DependentPromise thenApplyAsync(Function fn, Executor executor) { return thenApplyAsync(fn, executor, defaultEnlistOrigin()); } @Override public DependentPromise thenAccept(Consumer action) { return thenAccept(action, defaultEnlistOrigin()); } @Override public DependentPromise thenAcceptAsync(Consumer action) { return thenAcceptAsync(action, defaultEnlistOrigin()); } @Override public DependentPromise thenAcceptAsync(Consumer action, Executor executor) { return thenAcceptAsync(action, executor, defaultEnlistOrigin()); } @Override public DependentPromise thenRun(Runnable action) { return thenRun(action, defaultEnlistOrigin()); } @Override public DependentPromise thenRunAsync(Runnable action) { return thenRunAsync(action, defaultEnlistOrigin()); } @Override public DependentPromise thenRunAsync(Runnable action, Executor executor) { return thenRunAsync(action, executor, defaultEnlistOrigin()); } @Override public DependentPromise thenCombine(CompletionStage other, BiFunction fn) { return thenCombine(other, fn, defaultEnlistOptions); } @Override public DependentPromise thenCombineAsync(CompletionStage other, BiFunction fn) { return thenCombineAsync(other, fn, defaultEnlistOptions); } @Override public DependentPromise thenCombineAsync(CompletionStage other, BiFunction fn, Executor executor) { return thenCombineAsync(other, fn, executor, defaultEnlistOptions); } @Override public DependentPromise thenAcceptBoth(CompletionStage other, BiConsumer action) { return thenAcceptBoth(other, action, defaultEnlistOptions); } @Override public DependentPromise thenAcceptBothAsync(CompletionStage other, BiConsumer action) { return thenAcceptBothAsync(other, action, defaultEnlistOptions); } @Override public DependentPromise thenAcceptBothAsync(CompletionStage other, BiConsumer action, Executor executor) { return thenAcceptBothAsync(other, action, executor, defaultEnlistOptions); } @Override public DependentPromise runAfterBoth(CompletionStage other, Runnable action) { return runAfterBoth(other, action, defaultEnlistOptions); } @Override public DependentPromise runAfterBothAsync(CompletionStage other, Runnable action) { return runAfterBothAsync(other, action, defaultEnlistOptions); } @Override public DependentPromise runAfterBothAsync(CompletionStage other, Runnable action, Executor executor) { return runAfterBothAsync(other, action, executor, defaultEnlistOptions); } @Override public DependentPromise applyToEither(CompletionStage other, Function fn) { return applyToEither(other, fn, defaultEnlistOptions); } @Override public DependentPromise applyToEitherAsync(CompletionStage other, Function fn) { return applyToEitherAsync(other, fn, defaultEnlistOptions); } @Override public DependentPromise applyToEitherAsync(CompletionStage other, Function fn, Executor executor) { return applyToEitherAsync(other, fn, executor, defaultEnlistOptions); } @Override public DependentPromise acceptEither(CompletionStage other, Consumer action) { return acceptEither(other, action, defaultEnlistOptions); } @Override public DependentPromise acceptEitherAsync(CompletionStage other, Consumer action) { return acceptEitherAsync(other, action, defaultEnlistOptions); } @Override public DependentPromise acceptEitherAsync(CompletionStage other, Consumer action, Executor executor) { return acceptEitherAsync(other, action, executor, defaultEnlistOptions); } @Override public DependentPromise runAfterEither(CompletionStage other, Runnable action) { return runAfterEither(other, action, defaultEnlistOptions); } @Override public DependentPromise runAfterEitherAsync(CompletionStage other, Runnable action) { return runAfterEitherAsync(other, action, defaultEnlistOptions); } @Override public DependentPromise runAfterEitherAsync(CompletionStage other, Runnable action, Executor executor) { return runAfterEitherAsync(other, action, executor, defaultEnlistOptions); } @Override public DependentPromise thenCompose(Function> fn) { return thenCompose(fn, defaultEnlistOrigin()); } @Override public DependentPromise thenComposeAsync(Function> fn) { return thenComposeAsync(fn, defaultEnlistOrigin()); } @Override public DependentPromise thenComposeAsync(Function> fn, Executor executor) { return thenComposeAsync(fn, executor, defaultEnlistOrigin()); } @Override public DependentPromise exceptionally(Function fn) { return exceptionally(fn, defaultEnlistOrigin()); } @Override public DependentPromise whenComplete(BiConsumer action) { return whenComplete(action, defaultEnlistOrigin()); } @Override public DependentPromise whenCompleteAsync(BiConsumer action) { return whenCompleteAsync(action, defaultEnlistOrigin()); } @Override public DependentPromise whenCompleteAsync(BiConsumer action, Executor executor) { return whenCompleteAsync(action, executor, defaultEnlistOrigin()); } @Override public DependentPromise handle(BiFunction fn) { return handle(fn, defaultEnlistOrigin()); } @Override public DependentPromise handleAsync(BiFunction fn) { return handleAsync(fn, defaultEnlistOrigin()); } @Override public DependentPromise handleAsync(BiFunction fn, Executor executor) { return handleAsync(fn, executor, defaultEnlistOrigin()); } @Override public DependentPromise dependent() { return dependent(PromiseOrigin.NONE); } public DependentPromise dependent(Set defaultEnlistOptions) { if (null == defaultEnlistOptions) { defaultEnlistOptions = PromiseOrigin.NONE; } if (identicalSets(defaultEnlistOptions, this.defaultEnlistOptions)) { return this; } else { return ConfigurableDependentPromise.from( null == cancellableOrigins || cancellableOrigins.length == 0 ? delegate : cancellablePromiseOf(delegate), defaultEnlistOptions ); } } @Override public boolean cancel(boolean mayInterruptIfRunning) { if (delegate.cancel(mayInterruptIfRunning)) { cancelPromises(cancellableOrigins, mayInterruptIfRunning); return true; } else { return false; } } @Override public boolean isCancelled() { return delegate.isCancelled(); } @Override public boolean isDone() { return delegate.isDone(); } @Override public T get() throws InterruptedException, ExecutionException { return delegate.get(); } @Override public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return delegate.get(timeout, unit); } @Override public T getNow(T valueIfAbsent) throws CancellationException, CompletionException { return delegate.getNow(valueIfAbsent); } @Override public T getNow(Supplier valueIfAbsent) throws CancellationException, CompletionException { return delegate.getNow(valueIfAbsent); } @Override public T join() throws CancellationException, CompletionException { return delegate.join(); } @Override public Promise raw() { if (null == cancellableOrigins || cancellableOrigins.length == 0) { // No state collected, may optimize away own reference return delegate.raw(); } else { return cancellablePromiseOf(delegate.raw()); } } protected Promise cancellablePromiseOf(Promise original) { return new UndecoratedCancellationPromise<>(original, cancellableOrigins); } @Override public CompletableFuture toCompletableFuture() { return toCompletableFuture(defaultEnlistOrigin()); } public CompletableFuture toCompletableFuture(boolean enlistOrigin) { if (!enlistOrigin) { return delegate.toCompletableFuture(); } else { FutureCompletion result = new FutureCompletion().dependsOn(this); whenComplete((r, e) -> { if (null != e) { result.completeExceptionally(e); } else { result.complete(r); } }); return result; } } private CompletionStage[] origin(boolean enlist) { if (enlist) { CompletionStage[] result = new CompletionStage[1]; result[0] = this; return result; } else { return null; } } private CompletionStage[] originAndParam(CompletionStage param, Set enlistOptions) { final CompletionStage[] result = new CompletionStage[enlistOptions.size()]; int idx = 0; if (enlistOptions.contains(PromiseOrigin.THIS)) { result[idx++] = this; } if (enlistOptions.contains(PromiseOrigin.PARAM) && param != null) { result[idx++] = param; } return result; } private boolean defaultEnlistOrigin() { return defaultEnlistOptions.contains(PromiseOrigin.THIS); } static void cancelPromises(CompletionStage[] promises, boolean mayInterruptIfRunning) { if (null != promises) { Arrays.stream(promises) .filter(p -> p != null) .forEach(p -> cancelPromise(p, mayInterruptIfRunning)); } } private static boolean identicalSets(Set a, Set b) { return a.containsAll(b) && b.containsAll(a); } static class UndecoratedCancellationPromise extends AbstractPromiseDecorator> { private final CompletionStage[] dependent; UndecoratedCancellationPromise(Promise original, CompletionStage[] dependent) { super(original); this.dependent = dependent; } @Override public boolean cancel(boolean mayInterruptIfRunning) { if (super.cancel(mayInterruptIfRunning)) { cancelPromises(dependent, mayInterruptIfRunning); return true; } else { return false; } } @Override public Promise raw() { // May not unwrap further return this; } @Override protected Promise wrap(CompletionStage original) { // No wrapping by definition return (Promise)original; } } }