// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static boolean cancelPromise(CompletionStage<?> promise, boolean mayInterruptIfRunning) {
//     if (promise instanceof Future) {
//         Future<?> future = (Future<?>) promise;
//         return future.cancel(mayInterruptIfRunning);
//     } else {
//         return CancelMethodsCache.cancellationOf(promise.getClass())
//                                  .apply(promise, mayInterruptIfRunning);
//     }
// }
// 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static void iif(boolean v) {}
// 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static Throwable unwrapCompletionException(Throwable ex) {
//     Throwable nested = ex;
//     while (nested instanceof CompletionException) {
//         nested = nested.getCause();
//     }
//     return null == nested ? ex : nested;
// }
// 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static Throwable unwrapExecutionException(Throwable ex) {
//     Throwable nested = ex;
//     while (nested instanceof ExecutionException) {
//         nested = nested.getCause();
//     }
//     return null == nested ? ex : nested;
// }
// 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static ExecutionException wrapExecutionException(Throwable e) {
//     if (e instanceof ExecutionException) {
//         return (ExecutionException) e;
//     } else {
//         return new ExecutionException(e);
//     }
// }


import static net.tascalate.concurrent.SharedFunctions.cancelPromise;
import static net.tascalate.concurrent.SharedFunctions.iif;
import static net.tascalate.concurrent.SharedFunctions.unwrapCompletionException;
import static net.tascalate.concurrent.SharedFunctions.unwrapExecutionException;
import static net.tascalate.concurrent.SharedFunctions.wrapExecutionException;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
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;
                    if (e != failure) {
                        e.addSuppressed(failure);
                    }
                    return forwardException(e);
                }
            },
            executor
        );
    }    

    @Override
    public <U> Promise<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) {

        AbstractCompletableTask<Void> tempStage = newSubTask(executor);
        AbstractCompletableTask<U> nextStage = newSubTask(executor);
        // Need to enlist tempStage while it is non-visible outside
        // and may not be used to interrupt fn.apply();
        nextStage.intermediateStage = tempStage;

        // We must ALWAYS run through the execution
        // of nextStage.task when this nextStage is
        // exposed to the client, even in a "trivial" case:
        // Success path, just return value
        Consumer<? super U> onResult = nextStage.runTransition(Function.identity());
        // Failure path, just re-throw exception
        Consumer<? super Throwable> onError = nextStage.runTransition(forwardException());

        // Important -- tempStage is the target here
        addCallbacks(
            tempStage, 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static boolean cancelPromise(CompletionStage<?> promise, boolean mayInterruptIfRunning) {
//     if (promise instanceof Future) {
//         Future<?> future = (Future<?>) promise;
//         return future.cancel(mayInterruptIfRunning);
//     } else {
//         return CancelMethodsCache.cancellationOf(promise.getClass())
//                                  .apply(promise, mayInterruptIfRunning);
//     }
// }
// 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static void iif(boolean v) {}
// 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static Throwable unwrapCompletionException(Throwable ex) {
//     Throwable nested = ex;
//     while (nested instanceof CompletionException) {
//         nested = nested.getCause();
//     }
//     return null == nested ? ex : nested;
// }
// 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static Throwable unwrapExecutionException(Throwable ex) {
//     Throwable nested = ex;
//     while (nested instanceof ExecutionException) {
//         nested = nested.getCause();
//     }
//     return null == nested ? ex : nested;
// }
// 
// Path: src/main/java/net/tascalate/concurrent/SharedFunctions.java
// static ExecutionException wrapExecutionException(Throwable e) {
//     if (e instanceof ExecutionException) {
//         return (ExecutionException) e;
//     } else {
//         return new ExecutionException(e);
//     }
// }


// Path: src/main/java/net/tascalate/concurrent/AbstractCompletableTask.java
import static net.tascalate.concurrent.SharedFunctions.cancelPromise;
import static net.tascalate.concurrent.SharedFunctions.iif;
import static net.tascalate.concurrent.SharedFunctions.unwrapCompletionException;
import static net.tascalate.concurrent.SharedFunctions.unwrapExecutionException;
import static net.tascalate.concurrent.SharedFunctions.wrapExecutionException;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
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;

                    if (e != failure) {
                        e.addSuppressed(failure);
                    }
                    return forwardException(e);
                }
            },
            executor
        );
    }    

    @Override
    public <U> Promise<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) {

        AbstractCompletableTask<Void> tempStage = newSubTask(executor);
        AbstractCompletableTask<U> nextStage = newSubTask(executor);
        // Need to enlist tempStage while it is non-visible outside
        // and may not be used to interrupt fn.apply();
        nextStage.intermediateStage = tempStage;

        // We must ALWAYS run through the execution
        // of nextStage.task when this nextStage is
        // exposed to the client, even in a "trivial" case:
        // Success path, just return value
        Consumer<? super U> onResult = nextStage.runTransition(Function.identity());
        // Failure path, just re-throw exception
        Consumer<? super Throwable> onError = nextStage.runTransition(forwardException());

        // Important -- tempStage is the target here
        addCallbacks(
            tempStage, 
            r -> {