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

com.ajjpj.afoundation.concurrent.AFuture Maven / Gradle / Ivy

The newest version!
package com.ajjpj.afoundation.concurrent;

import com.ajjpj.afoundation.collection.tuples.ATuple2;
import com.ajjpj.afoundation.collection.tuples.ATuple3;
import com.ajjpj.afoundation.function.AFunction1;
import com.ajjpj.afoundation.function.AStatement1NoThrow;
import com.ajjpj.afoundation.function.AStatement2NoThrow;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


/**
 * An instance of AFuture represents a result that may or may not be available yet. Its methods can be
 *  categorized as follows:
 * 
    *
  • Interaction with the underlying computation. A call to {@link #cancel(boolean)} cancels that computation unless it is already finished, while {@link #isCancelled()} * and {@link #isFinished()} check if the computation was cancelled or finished in whatever way, respectively. *
  • Blocking access to the result. Calling {@link #get()} with or without a timeout will return the result, blocking until it becomes available if necessary. This is * pretty crude, and it wastes resources by blocking a thread. However it makes for straightforward code - but you should consider registering a callback instead. *
  • Registering callbacks. Calling {@link #onSuccess(com.ajjpj.afoundation.function.AStatement1NoThrow)}, {@link #onFailure(com.ajjpj.afoundation.function.AStatement1NoThrow)} or * {@link #onFinished(com.ajjpj.afoundation.function.AStatement2NoThrow)} registers a callback that is triggered when the future completes successfully, fails (i.e. the task * threw an exception or timed out}, or finished either way. Using these callbacks is the preferred way of dealing with results because it does not block any threads. * Callbacks are guaranteed to be executed even if they are registered after the future's computation is finished. *
  • Creating a new AFuture base on this future's result without waiting for it to finish first. You can provide a default value in case this future's * computation fails or times out ({@link #withDefaultValue(Object)}), or you can transform the result {@link #mapSync(com.ajjpj.afoundation.function.AFunction1) synchronously} * or {@link #mapAsync(com.ajjpj.afoundation.function.AFunction1, long, java.util.concurrent.TimeUnit) asynchronously}. You can also call one of the zip methods * to combine {@link #zip(AFuture) two} or {@link #zip(AFuture, AFuture) more} AFutures into one.

    * This way of working with futures as if they were actual values, but without blocking any threads to wait for intermediate results is one of the key benefits of * using {@link AThreadPool} and AFuture. {@link AFutureHelper} contains more methods for working with AFuture instances. *

* * @author arno */ public interface AFuture { /** * This method cancels the underlying computation unless it is finished already. If the task is not yet running, this method ensures that it never will. * @param mayInterruptIfRunning determines whether the underlying thread pool should attempt to interrupt the worker thread if the underlying task is currently running. */ boolean cancel (boolean mayInterruptIfRunning); /** * @return true if and only if this future was cancelled. */ boolean isCancelled (); /** * @return true if and only if this future's task is finished, be it through cancellation, success, failure or timeout. */ boolean isFinished (); /** * @return a new AFuture with a lifecycle that is bound to this AFuture's. The only difference is that if this future fails, the returned * future will instead finish successfully with the provided defaultValue. */ AFuture withDefaultValue (T defaultValue); /** * This method returns the future's result, performing a blocking wait until it becomes available. While this waiting looks like it is unbounded, it is actually bounded * by the underlying task's timeout. Nonetheless, this method should be used with extreme care because it can block the caller thread for a potentially very long time. * * @throws java.lang.InterruptedException if the underlying task was interrupted * @throws java.util.concurrent.ExecutionException if the underlying task failed. The actual exception causing the failure is wrapped in the ExecutionException. */ T get () throws InterruptedException, ExecutionException; /** * This method returns the future's result, performing a blocking wait until it becomes available or this method's timeout is reached. This method should be used with extreme * care because it can block the caller thread. * * @throws java.lang.InterruptedException if the underlying task was interrupted * @throws java.util.concurrent.ExecutionException if the underlying task failed. The actual exception causing the failure is wrapped in the ExecutionException. * @throws java.util.concurrent.TimeoutException if this method's timeout was reached */ T get(long timeout, TimeUnit timeoutUnit) throws InterruptedException, ExecutionException, TimeoutException; /** * Registers a callback that is called if and when the future completes successfully.

* * It is permissible to register several callbacks, in which case they are all called sequentially. Callbacks are guaranteed to be called, even if they are registered * after a future finished.

* * Callbacks are called in a 'borrowed' thread, i.e. either the task's thread (if they are registered before the task is finished) or the caller's thread (if they are * registered after the task is finished). They should typically do only safe and fast work in place, submitting anything else as separate tasks. */ void onSuccess (AStatement1NoThrow callback); /** * Registers a callback that is called if and when the future fails, i.e. the task throws an exception or times out. The callback receives the cause of failure as a * parameter.

* * It is permissible to register several callbacks, in which case they are all called sequentially. Callbacks are guaranteed to be called, even if they are registered * after a future finished.

* * Callbacks are called in a 'borrowed' thread, i.e. either the task's thread (if they are registered before the task is finished) or the caller's thread (if they are * registered after the task is finished). They should typically do only safe and fast work in place, submitting anything else as separate tasks. */ void onFailure (AStatement1NoThrow callback); /** * Registers a callback that is called when a future finishes - either successfully or by failing. On success, the callback receives the future's value as its first parameter * while the second parameter is null. On failure, the first paramter is null while the second paramter contains the cause of failure. The * canonical way to distinguish between the two is to compare the second parameter with null.

* * It is permissible to register several callbacks, in which case they are all called sequentially. Callbacks are guaranteed to be called, even if they are registered * after a future finished.

* * Callbacks are called in a 'borrowed' thread, i.e. either the task's thread (if they are registered before the task is finished) or the caller's thread (if they are * registered after the task is finished). They should typically do only safe and fast work in place, submitting anything else as separate tasks. */ void onFinished (AStatement2NoThrow callback); /** * Creates a new AFuture instance with a lifecycle that is bound to this AFuture's lifecycle, applying a given function f to the result. * This method does for futures what {@link com.ajjpj.afoundation.collection.immutable.ACollection#map(com.ajjpj.afoundation.function.AFunction1)} does for collecctions.

* * This method causes the transformation function to be applied in the original future's thread and is thus outside of timeout checks. For fast operations, this saves * scheduling overhead. For non-trivial transformations, use {@link #mapAsync(com.ajjpj.afoundation.function.AFunction1, long, java.util.concurrent.TimeUnit) mapAsync} instead. */ AFuture mapSync (AFunction1 f); /** * Creates a new AFuture. If this future fails, the newly created future fails as well. If this future completes successfully, a new task is scheduled, applying * the transformation function to this future's result. This method does for futures what * {@link com.ajjpj.afoundation.collection.immutable.ACollection#map(com.ajjpj.afoundation.function.AFunction1)} does for collecctions.

* * Scheduling is done in this future's underlying thread pool, using the timeout parameter. */ AFuture mapAsync (AFunction1 f, long timeout, TimeUnit timeoutUnit); /** * Creates a new AFuture. If this future fails, the newly created future fails as well. If this future completes successfully, a new task is scheduled, applying * the transformation function to this future's result. This method does for futures what * {@link com.ajjpj.afoundation.collection.immutable.ACollection#map(com.ajjpj.afoundation.function.AFunction1)} does for collecctions.

* * Scheduling is done in the explicitly provided thread pool, using the timeout parameter. */ AFuture mapAsync (AFunction1 f, ATaskScheduler threadPool, long timeout, TimeUnit timeoutUnit); /** * Combines this AFuture with another AFuture into a single future with an {@link com.ajjpj.afoundation.collection.tuples.ATuple2} of the two values. If either * of the original futures fails, the newly created future fails. If both futures succeed, the newly created future succeeds with the combined value. */ AFuture> zip (AFuture other); /** * Combines this AFuture with two other AFutures into a single future with an {@link com.ajjpj.afoundation.collection.tuples.ATuple3} of the three values. * If one of the original futures fails, the newly created future fails. If all three futures succeed, the newly created future succeeds with the combined value.

* * If you want to combine more than three futures into a single future, use {@link AFutureHelper#lift(java.util.Collection)}. */ AFuture> zip (AFuture other1, AFuture other2); }