/* This file is part of Reactive Cascade which is released under The MIT License. See license.md , https://github.com/futurice/cascade and http://reactivecascade.com for details. This is open source for the common good. Please contribute improvements by pull request or contact paulirotta@gmail.com */ package com.reactivecascade.i; import android.support.annotation.CheckResult; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.reactivecascade.functional.RunnableAltFuture; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * One of many possible futures in the multiverse. {@link java.util.concurrent.Future}. * A {@link java.util.concurrent.Future} that follows an alternative contract split supports a slightly different * set of methods split exception handling. * <p> * A traditional * <code>Future</code> @see <a href="http://developer.android.com/reference/java/util/concurrent/Future.html">Java Future</a> * is blocking when referencing the from. It turns out others * have been trying to very similarly work past the limitations of Future by executing a callback on completion, notably Java 8 with * <code>CompletableFuture</code> @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html">Java 8 CompletableFuture</a>, * the Guava library's * <code>ListenableFuture</code> @see <a href="http://google.github.io/guava/releases/snapshot/api/docs/com/google/common/util/concurrent/ListenableFuture.html">Guava ListenableFuture</a>, * and Scala's implementation of * <code>Future</code> @see <a href="http://www.scala-lang.org/files/archive/nightly/docs/library/index.html#scala.concurrent.Future">Scala Future</a>. * All three of these solutions are suitable for server development. Guava configured with appropriate Proguard * minimization is suitable for Android. * <p> * <code>RunnableAltFuture</code> differs from the above alternatives by providing an execution model and strictly defined inter-thread * communication and error handling contract. You may want think of this as aspect-oriented programming with * each <code>RunnableAltFuture</code> in a functional chain strictly associated with an {@link com.reactivecascade.i.IThreadType}. * The defined thread type (a named group of one or more threads) explicitly sets the maximum concurrency contract. * The ideal concurrency for a given step in a functional chain is determined by the primary limiting * resource of that purely functional computational step or side effect. This can be determined and fixed at * the time the <code>IAltFuture</code> is defined. The limiting resource may be the * necessity for a piece of code to execute synchronously after other UI code on the application's main thread. * If this is not required, then the number of CPU cores on the execution * device or other primary resource constraint such to that thread type such as the optimal number of * concurrent network write operations for current network conditions. Used properly, this should lead to work throughput increases, * reduced work in progress, latency reduction, and peak memory use bounding while keeping the code simple and highly * productive. * <p> * Non-cooperative interrupts and blocking actions like * {@link java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)} are not allowed in this model. * These are replaced with an explicit dependency <code>.then()</code> action chain with simple asynchronous exception * handling. * <p> * Instead, use {@link IAltFuture#get()}. If the <code>IAltFuture</code> has not yet completed * execution successfully it will immediately throw a {@link java.lang.IllegalStateException} * at run time. This allows you to make your lambdas more simple, but still handle asynchronous * by functional chaining. * <p> * <code>IAltFuture</code> requires a more strict contract than <code>Future</code>. While a * <code>Future</code> allows you to "merge" (wait for) a task which has not yet completed, * <code>IAltFuture</code> requires execution to the point of returning such a prerequisite from to * complete before the request to {@link IAltFuture#get()} is made. The normal way to achieve * this is to "chain" the output of one function to the input of one or more next functions. For example * {@link IAltFuture#map(IActionOneR)} will create an <code>RunnableAltFuture</code> which * will receive as input the output of <code>this</code>, process it split output another from in turn. * <p> * <code>IAltFuture</code> implementations are compatible with {@link Future}. The default implementation * {@link RunnableAltFuture} is not a <code>Future</code> to avoid confusion. */ public interface IAltFuture<IN, OUT> extends ICancellable, ISafeGettable<OUT>, IAsyncOrigin { /** * A method which returns a new (unforked) <code>IAltFuture</code> should follow the naming conventiond <code>..Async</code> * and be annotated <code>@CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION)</code> to {@link CheckResult} that * it is either stored or manually {@link #fork()}ed. * <p> * TODO This annotation is not ideal for disambiguating all cases, look at creating a new one */ String CHECK_RESULT_SUGGESTION = "IAltFuture#fork()"; /** * Find which thread pool and related support functions are used * * @return thread group */ @NonNull IThreadType getThreadType(); /** * Find if the final, immutable state has been entered either with a successful result or an error * code * * @return <code>true</code> once final state has been determined */ boolean isDone(); /** * Find if this object has already been submitted to an executor. Execution may finish at any time, * or already have finished if this is true. * * @return <code>true</code> once queued for execution */ boolean isForked(); /** * Place this {@link IAltFuture} in the ready-to-run-without-blocking * queue of its {@link com.reactivecascade.i.IThreadType}. If there is a {@link #getUpchain()} * subscribe that will be forked instead until finding one where {@link #isDone()} is false. * * @return <code>this</code>, which is usually the <code>IAltFuture</code> which was actually forked. * The fork may be indirect and only after other items complete because there is a search upchain * (which may be a different branch spliced into the current chain) to find the first unforked from. */ @NonNull IAltFuture<IN, OUT> fork(); /** * Find the previous step in the chain. * <p> * Once {@link #isDone()}, this may return <code>null</code> even if there was a previous alt future * at one point. This is done to allow a chain to reduce peak load and increase memory throughput * by freeing memory of previous steps as it goes. * * @return the previous {@link IAltFuture} in the chain, or <code>null</code> if this is currently * the head of the chain */ @Nullable IAltFuture<?, ? extends IN> getUpchain(); /** * This is done for you when you add functions to a chain. You do not need to call it yourself. * <p> * The implementation may call this multiple times to support merging chains. This happens most * often when a method returns the tail of a section of chain. The returned from in this case is * a new chain link stopping the merged chain section which might start burning before the merger * is created to not burn past the merger point until the primary chain reaches that point also. * * @param altFuture the previous alt future in the chain to which this is attached */ void setUpchain(@NonNull IAltFuture<?, ? extends IN> altFuture); /** * Notification from an up-chain {@link IAltFuture} that the stream is broken * and will not complete normally. This RunnableAltFuture will be set to an error state. * <p> * If an mOnError or catch method has been defined, it will be * notified of the original cause of the failure. If the RunnableAltFuture's mOnError method consumes the error * (returns <code>true</code>), subscribe anything else down-chain methods will be notified with * {@link #onCancelled(StateCancelled)} instead. * * @param stateError the state indicating the reason and origin of the exception * @throws Exception if there is a problem performing synchronous downchain notifications */ void onError(@NonNull StateError stateError) throws Exception; /** * Notification indicates an up-chain {@link IAltFuture} has been cancelled. * This RunnableAltFuture will be set to a cancelled state and not be given a chance to complete normally. * All down-chain AltFutures will similarly be notified that they were cancelled. * * @param stateCancelled the state indicating the reason and origin of cancellation * @throws Exception if there is a problem performing synchronous downchain notifications */ void onCancelled(@NonNull StateCancelled stateCancelled) throws Exception; /** * Switch following (downchain) actions to run on the specified {@link IThreadType} * * @param theadType the thread execution group to change to for the next chain operation * @return the previous chain link alt future from continuing the chain on the new {@link IThreadType} */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) IAltFuture<?, OUT> on(@NonNull IThreadType theadType); /** * Execute the mOnFireAction after this <code>RunnableAltFuture</code> finishes. * * @param action function to be performed, often a lambda statement * @return an alt future representing the eventual output value of the action */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) IAltFuture<OUT, OUT> then(@NonNull IAction<OUT> action); @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) @SuppressWarnings("unchecked") ISettableAltFuture<OUT> then(@NonNull IAction<? extends OUT>... actions); /** * Execute the action after this <code>RunnableAltFuture</code> finishes. * * @param action function to be performed, often a lambda statement * @return an alt future representing the eventual output value of the action */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) IAltFuture<OUT, OUT> then(@NonNull IActionOne<OUT> action); @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) @SuppressWarnings("unchecked") ISettableAltFuture<OUT> then(@NonNull IActionOne<OUT>... actions); /** * Execute the mOnFireAction after this <code>RunnableAltFuture</code> finishes. * * @param action function to be performed, often a lambda statement * @return an alt future representing the eventual output value of the action */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <DOWNCHAIN_OUT> IAltFuture<OUT, DOWNCHAIN_OUT> then(@NonNull IActionR<DOWNCHAIN_OUT> action); /** * Start an action wrapped as an alt futre after this alt future completes. This next alt future * may be on a different {@link IThreadType} thread pool. * <p> * Multiple altFutures may be chained after the current alt future. They will be fork()ed in the * orderin which they were attached. Depending on their thread type, the resulting execution may * be concurrent or serial. * * @param altFuture the next action in the chain * @param <DOWNCHAIN_OUT> the type that the OUT value will be mapped to. This may not be a mapping alt future, in which case the type DOWNCHAIN_OUT = OUT and the value is also identical * @return altFuture */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <DOWNCHAIN_OUT> IAltFuture<OUT, DOWNCHAIN_OUT> then(@NonNull IAltFuture<OUT, DOWNCHAIN_OUT> altFuture); /** * Execute the mOnFireAction after this <code>RunnableAltFuture</code> finishes. * * @param action function to be performed, often a lambda statement * @param <DOWNCHAIN_OUT> the type that the OUT value will be mapped to * @return an alt future value representing the eventual output from the mapping function */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <DOWNCHAIN_OUT> IAltFuture<OUT, DOWNCHAIN_OUT> map(@NonNull IActionOneR<OUT, DOWNCHAIN_OUT> action); @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) @SuppressWarnings("unchecked") <DOWNCHAIN_OUT> IAltFuture<OUT, DOWNCHAIN_OUT>[] map(@NonNull IActionOneR<OUT, DOWNCHAIN_OUT>... actions); /** * Pause execution of this chain for a fixed time interval. Other chains will be able to execute * in the meanwhile- no threads are blocked. * <p> * Note that the chain realizes immediately in the event of {@link #cancel(String)} or a runtime error * * @param sleepTime to pause the chain, in timeUnit values * @param timeUnit to pause * @return the input value from upchain */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) ISettableAltFuture<OUT> sleep(long sleepTime, @NonNull TimeUnit timeUnit); /** * Continue to next step(s) in the chain only after the {@link IAltFuture} being waited for is complete. The from * is not propaged in the chain, but if there is an error that will be adopted into this chain. * * @param altFuture to pause this chain until it <code>{@link #isDone()}</code> * @return the upchain value of this chain, execution held until the other alt future completes */ // @NonNull // @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) // ISettableAltFuture<OUT> await(@NonNull IAltFuture<?, ?> altFuture); /** * Continue chain execution once all upchain futures realize and have completed their side effects. * <p> * The await operation will be cancelled or exception state and trigger {@link #onError(IActionOne)} * if <em>any</em> of the joined operations have a problem running to completion. * * @param altFutures to pause this chain until they are done (<code>{@link #isDone()}</code>) * @return the upchain value of this chain, execution held until other alt futures complete */ @NonNull @SuppressWarnings("unchecked") @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) ISettableAltFuture<OUT> await(@NonNull IAltFuture<?, ?>... altFutures); /** * Pass through to the next function only if element meet a logic test, otherwise {@link #cancel(String)} * * @param action function to be performed, often a lambda statement * @return the input value, now guaranteed to meet the logic test */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) IAltFuture<IN, IN> filter(@NonNull IActionOneR<IN, Boolean> action); /** * Set the reactiveTarget (one time only) when this is asserted * * @param reactiveTarget * @return <code>this</code> */ @NonNull IAltFuture<OUT, OUT> set(@NonNull IReactiveTarget<OUT> reactiveTarget); /** * Add an action which will be performed if this AltFuture or any AltFuture up-chain either has * a runtime error or is {@link #cancel(String)}ed. * <p> * This is typically a user notification or cleanup such as removing an ongoing process indicator (spinner). * * @param action function to be performed, often a lambda statement * @return <code>this</code> */ @NonNull ISettableAltFuture<OUT> onError(@NonNull IActionOne<Exception> action); /** * Add an action which will be performed if this AltFuture or any AltFuture up-chain either has * a runtime error or is {@link #cancel(String)}ed. * <p> * This is typically used for cleanup such as changing the screen to notify the user or remove * an ongoing process indicator (spinner). * * @param action function to be performed, often a lambda statement * @return <code>this</code> */ @NonNull ISettableAltFuture<OUT> onCancelled(@NonNull IActionOne<String> action); }