package io.trane.future;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.function.Supplier;
/**
* A `FuturePool` isolates portions of a `Future` composition on a
* separate thread pool.
*
* Example:
*
* ```java
* FuturePool futurePool = FuturePool.apply(Executors.newCachedThreadPool());
*
* Future<List<Token>> user =
* documentService.get(docId)
* .flatMap(doc -> futurePool.async(tokenize(doc)))
* ```
*
* This feature useful to isolate cpu-intensive tasks and blocking
* operations. Please refer to the Java documentation to decide
* which type of executor is the best for the kind of task that
* needs to be performed. For instance, a `ForkJoinPool` is useful
* for cpu-intensive tasks, but can't be used for blocking operations.
*
* The `FuturePool` also has the method `isolate` that isolates
* the execution of a `Future`:
*
* ```java
* FuturePool futurePool = FuturePool.apply(Executors.newCachedThreadPool());
*
* Future<User> user = futurePool.isolate(userRepo.get(userId));
* ```
*
* `isolate` is just a shortcut for `async` + `Future.flatten`.
*/
public final class FuturePool {
private final ExecutorService executor;
/**
* Creates a new future pool.
* @param executor the executor used to schedule tasks.
* @return the new future pool.
*/
public static FuturePool apply(final ExecutorService executor) {
return new FuturePool(executor);
}
private FuturePool(final ExecutorService executor) {
this.executor = executor;
}
/**
* Isolates the execution of a future on this future pool.
* @param s the supplier that creates the future.
* @return the isolated future.
*/
public final <T> Future<T> isolate(final Supplier<Future<T>> s) {
return Future.flatten(async(s));
}
/**
* Isolates the execution of the supplier on this future pool.
* @param s the supplier.
* @return a future with the result of the supplier.
*/
public final <T> Future<T> async(final Supplier<T> s) {
try {
final AsyncPromise<T> p = new AsyncPromise<>(s);
executor.submit(p);
return p;
} catch (final RejectedExecutionException ex) {
return Future.exception(ex);
}
}
}
final class AsyncPromise<T> extends Promise<T> implements Runnable {
private final Supplier<T> s;
public AsyncPromise(final Supplier<T> s) {
super();
this.s = s;
}
@Override
public final void run() {
setValue(s.get());
}
}