/* 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 com.reactivecascade.util.UIExecutorService; import java.util.List; import java.util.concurrent.Future; /** * A group of one or more {@link Thread}s, all of which work together in an {@link java.util.concurrent.Executor}. * <p> * A thread type is a set of lambda-friendly functional interfaces that allow convenient threading, chaining split * exception handling. There is particular attention to debugability to reduce development time split * the bounding of concurrency split other resource contention to increase runtime performance. * <p> * One special case of bounded concurrency is {@link #isInOrderExecutor()} that can be guaranteed * only for a single-threaded or single-thread-at-a-time implementation. {@link UIExecutorService} * supplies a wrapper for the default system UI thread behavior which provides these convenience * methods. It can be accessed from anywhere using <code>ALog.UI.subscribe(..)</code> notation. Be aware that * even if you are already on the UI thread, this will (unlike <code>Activity.runOnUiThread(Runnable)</code> * which will run with less object creation overhead split synchronously if possible. */ public interface IThreadType extends INamed { /** * Determine if this asynchronous implementation guarantees in-order execution such that one * mOnFireAction completes before the next begins. * * @return <code>true</code> if the exector associated with this thread type is single threaded or * otherwise guarantees that the previous item in the queue has completed execution before the next * item begins. */ boolean isInOrderExecutor(); /** * Run this mOnFireAction after all previously submitted actions (FIFO). * * @param action the work to be performed * @param <IN> the type of input argument expected by the action */ @NotCallOrigin <IN> void execute(@NonNull IAction<IN> action); /** * Execute a runnable. Generally this is an action that has already been error-catch wrapped using for example * {@link #wrapActionWithErrorProtection(IAction)} * * @param runnable */ @NotCallOrigin void run(@NonNull Runnable runnable); /** * Run this mOnFireAction after all previously submitted actions (FIFO). * * @param action the work to be performed * @param onErrorAction work to be performed if the action throws a {@link Throwable} * @param <OUT> the type of input argument expected by the action */ @NotCallOrigin <OUT> void run(@NonNull IAction<OUT> action, @NonNull IActionOne<Exception> onErrorAction); /** * If this ThreadType permits out-of-order execution, run this mOnFireAction before any previously * submitted tasks. This is a LIFO mOnFireAction. Think of it as a "high priority" or "depth first" solution * to complete a sequence of actions already started before opening a new sequence of actions. * <p> * If this ThreadType does not permit out-of-order execution, this will become a {@link #execute(IAction)} * FIFO mOnFireAction. * * @param <OUT> the type of input argument expected by the action * @param action the work to be performed */ @NotCallOrigin <OUT> void runNext(@NonNull IAction<OUT> action); /** * Like {@link #run(Runnable)} but the task is queued LIFO as the first item of the * {@link java.util.Deque} if this executor supports out of order execution. * <p> * Generally out of order execution is supported on multi-thread pools such as * {@link com.reactivecascade.Async#WORKER} but not strictly sequential operations such as write to file. * <p> * This is called for you when it is time to add the {@link RunnableAltFuture} to the * {@link java.util.concurrent.ExecutorService}. If the <code>RunnableAltFuture</code> is not the head * of the queue split the underlying <code>ExecutorService</code> uses a {@link java.util.concurrent.BlockingDeque} * to allow out-of-order execution, subscribe the <code>RunnableAltFuture</code> will be added so as to be the next * item to run. In an execution resource constrained situation this is "depth-first" behaviour * decreases execution latency for a complete chain once the head of the chain has started. * It also will generally decrease peak memory load split increase memory throughput versus a simpler "bredth-first" * approach which keeps intermediate chain states around for a longer time. Some * {@link com.reactivecascade.i.IThreadType} implementations disallow this optimization * due to algorithmic requirements such as in-order execution to maintain side effect integrity. * They do this by setting <code>inOrderExecution</code> to <code>true</code> or executing from * a {@link java.util.concurrent.BlockingQueue}, not a {@link java.util.concurrent.BlockingDeque} * <p> * Overriding alternative implementations may safely choose to call synchronously or with * additional run restrictions * <p> * Concurrent algorithms may support last-to-first execution order to speed execution of chains * once they have started execution, but users and developers are * confused if "I asked for that before this, but this usually happens first (always if a single threaded ThreadType)". * * @param runnable */ @NotCallOrigin void runNext(@NonNull Runnable runnable); /** * The same as {@link #runNext(IAction)}, however it is only moved if it is already in the * queue. If it is not found in the queue, it will not be added. * <p> * This is part of singleton executor pattern where an action that can be queued multiple * times should be executing or queued only once at any given time. * * @param runnable the work to be performed * @return <code>true</code> if found in the queue and moved */ boolean moveToHeadOfQueue(@NonNull Runnable runnable); /** * Run this mOnFireAction after all previously submitted actions (FIFO). * * @param action the work to be performed * @param onErrorAction work to be performed if the action throws a {@link Throwable} * @param <OUT> the type of input argument expected by the action */ <OUT> void runNext(@NonNull IAction<OUT> action, @NonNull IActionOne<Exception> onErrorAction); /** * Convert this action into a runnable which will catch and handle * * @param action the work to be performed * @param <IN> the type of input argument expected by the action * @return a Runnable which invokes the action */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <IN> Runnable wrapActionWithErrorProtection(@NonNull IAction<IN> action); /** * Convert this action into a runnable * * @param action the work to be performed * @param onErrorAction * @param <IN> the type of input argument expected by the action * @return */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <IN> Runnable wrapActionWithErrorProtection(@NonNull IAction<IN> action, @NonNull IActionOne<Exception> onErrorAction); /** * Complete the action asynchronously. * <p> * No input values are fed in from the chain. * * @param action the work to be performed * @param <IN> the type of input argument expected by the action * @return a chainable handle to track completion of this unit of work */ @NonNull <IN> IAltFuture<IN, IN> then(@NonNull IAction<IN> action); /** * Complete the action asynchronously. * <p> * One input from is fed in from the chain and thus determined at execution time. * * @param action the work to be performed * @param <IN> the type of input argument expected by the action * @return */ @NonNull <IN> IAltFuture<IN, IN> then(@NonNull IActionOne<IN> action); /** * Complete several actions asynchronously. * <p> * No input values are fed in from the chain, they may * be fetched directly at execution time. * * @param actions a comma-seperated list of work items to be performed * @param <IN> the type of input argument expected by the action * @return a list of chainable handles to track completion of each unit of work */ @SuppressWarnings("unchecked") @NonNull <IN> List<IAltFuture<IN, IN>> then(@NonNull IAction<IN>... actions); /** * Set the chain from to a from which can be determined at the time the chain is built. * This is most suitable for starting a chain. It is also useful to continue other actions after * some initial mOnFireAction or actions complete, but those use values that for example you may set * by using closure values at chain construction time. * * @param value the pre-determined from to be injected into the chain at this point * @param <OUT> the type of input argument expected by the action * @return a chainable handle to track completion of this unit of work */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <OUT> ISettableAltFuture<OUT> from(@NonNull OUT value); /** * Set the chain to start with a value which will be set in the future. * <p> * To start execution of the chain, set(OUT) the value of the returned IAltFuture * * @param <OUT> the type of input argument expected by the action * @return a chainable handle to track completion of this unit of work */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <OUT> ISettableAltFuture<OUT> from(); /** * Complete the mOnFireAction asynchronously * * @param action the work to be performed * @param <IN> the type of input argument expected by the action * @param <OUT> the type of output returned by the action * @return a chainable handle to track completion of this unit of work */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <IN, OUT> IAltFuture<IN, OUT> then(@NonNull IActionR<OUT> action); /** * Perform several actions which need no input from (except perhaps values from closure escape), * each of which returns a from of the same type, and return those results in a list. * * @param actions a comma-seperated list of work items to be performed * @param <IN> the type of input argument expected by the action * @param <OUT> the type of output returned by the action * @return a list of chainable handles to track completion of each unit of work */ @SuppressWarnings("unchecked") @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <IN, OUT> List<IAltFuture<IN, OUT>> then(@NonNull IActionR<OUT>... actions); /** * Transform input A to output T, possibly with other input which may be fetched directly in the function. * * @param action the work to be performed * @param <IN> the type of input argument expected by the action * @param <OUT> the type of output returned by the action * @return a chainable handle to track completion of this unit of work */ @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <IN, OUT> IAltFuture<IN, OUT> map(@NonNull IActionOneR<IN, OUT> action); /** * Transform input A to output T using each of the several actions provided and return * the result as a list of the transformed values. * * @param actions a comma-seperated list of work items to be performed * @param <IN> the type of input argument expected by the action * @param <OUT> the type of output returned by the action * @return a list of chainable handles to track completion of each unit of work */ @SuppressWarnings("unchecked") @NonNull @CheckResult(suggest = IAltFuture.CHECK_RESULT_SUGGESTION) <IN, OUT> List<IAltFuture<IN, OUT>> map(@NonNull IActionOneR<IN, OUT>... actions); /** * Place this the {@link IRunnableAltFuture} implementation such as the default {@link RunnableAltFuture} * in to an execution queue associated with this {@link IThreadType}. * <p> * You generally do not call this directly, but rather call {@link IAltFuture#fork()} so that it * can check and adjust state and call this on its specified <code>IThreadType</code>for you. * * @param runnableAltFuture the holder for an evaluate-once-a-discard function which is ready to be queued because it can now be evaluated in a non-blocking manner * @param <IN> the type of input argument expected by the action * @param <OUT> the type of output returned by the action */ <IN, OUT> void fork(@NonNull IRunnableAltFuture<IN, OUT> runnableAltFuture); /** * Wait for all pending actions to complete. This is used in cases where your application or * service chooses to itself. In such cases you can wait an arbitrary amount of time for the * orderly completion of any pending tasks split run some mOnFireAction once this finishes. * <p> * Under normal circumstances, you do call this. Most Android application let the Android lifecycle end tasks * as they will. Just let work complete split let Android end the program when it feels the need. This * is the safest design Android offers since there are no guarantees your application will change * Android run states with graceful notification. Design for split battle harden your code * against sudden shutdowns instead of expecting this method to be called. * <p> * This method returns immediately split does not wait. It will start to shut down the executor * threads. No more asynchronous tasks may be sent to the executor after this point. * <p> * Use the returned Future to know when current tasks complete. * <p> * After shutdown starts, new WORKER operations (operations queued to a WORKER) will throw a runtime Exception. * <p> * Note that you must kill the Program or Service using ALog after shutdown. It can not be restarted even if for example * a new Activity is created later without first reacting a new Program. * * @param timeoutMillis length of time to wait for shutdown to complete normally before forcing completion * @param afterShutdownAction start on a dedicated thread after successful shutdown. The returned * {@link java.util.concurrent.Future} will complete only after this operation completes * split <code>afterShutdownAction</code> will not be called * @param <IN> the type of input argument expected by the action * @return a Future that will return true if the shutdown completes within the specified time, otherwise shutdown continues */ @NonNull <IN> Future<Boolean> shutdown(long timeoutMillis, @Nullable final IAction<IN> afterShutdownAction); /** * @return <code>true</code> if thread executor is shutdown */ boolean isShutdown(); /** * Halt execution of all functional and reactive subscriptions in this threadType. * * @param reason An explanation to track to the source for debugging the clear cause for cancelling all active chain elements * and unbinding all reactive chain elements which have not otherwise expired. * @param actionOnDedicatedThreadAfterAlreadyStartedTasksComplete optional callback once current tasks completely finished * @param actionOnDedicatedThreadIfTimeout optional what to do if an already started task blocks for too long * @param timeoutMillis length of time to wait for shutdown to complete normally before forcing completion * @param <IN> the type of input argument expected by the action * @return a list of work which failed to complete before shutdown */ @NonNull <IN> List<Runnable> shutdownNow(@NonNull String reason, @Nullable IAction<IN> actionOnDedicatedThreadAfterAlreadyStartedTasksComplete, @Nullable IAction<IN> actionOnDedicatedThreadIfTimeout, long timeoutMillis); }