com.github.tonivade.purefun.concurrent.Future Maven / Gradle / Ivy
/*
* Copyright (c) 2018-2020, Antonio Gabriel Muñoz Conejo
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.purefun.concurrent;
import com.github.tonivade.purefun.CheckedRunnable;
import com.github.tonivade.purefun.Consumer1;
import com.github.tonivade.purefun.Consumer2;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.HigherKind;
import com.github.tonivade.purefun.Matcher1;
import com.github.tonivade.purefun.Producer;
import com.github.tonivade.purefun.Sealed;
import com.github.tonivade.purefun.Unit;
import com.github.tonivade.purefun.type.Try;
import java.time.Duration;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.github.tonivade.purefun.Function1.cons;
import static com.github.tonivade.purefun.Function1.identity;
import static com.github.tonivade.purefun.Unit.unit;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.CompletableFuture.supplyAsync;
/**
* This type is an abstraction of a computation executed in another thread. To run the computation an {@code Executor}
* should be provided. If no {@code Executor} is provided, then a default instance is used.
*
* The result of the computation is a {@code Try}, this means that the computation can end successfully or with an error.
*
* You can create instances of {@code Future} in this way:
*
* - Future.success(value): returns a future that returns successfully with the given value.
* - Future.failure(error): returns a future that returns an error with the given error.
* - Future.async(computation): returns a future that eventually will execute the given computation.
* - Future.exec(runnable): returns a future that eventually will execute the given runnable.
* - Future.delay(duration, computation): returns a future that eventually will execute the given computation, but after waiting the given duration.
* - Future.defer(computation): returns a future that eventually will execute the given computation that returns another Future.
* - Future.bracket(acquire, usage, release): returns a future that eventually will acquire a resource, then use it, and finally release it.
*
*
* A future can be cancelable by calling the method {@code cancel}. If the future has not been executed yet, the future will be cancelled
* and the result of the computation will be a {@code Try.failure(CancellableException)}, but if the future has been executed, and is completed
* the calling of cancel method will not have any consequences. If the computation is running when the cancel method is called, and if the flag
* mayInterruptThread is true, then it will try to interrupt the thread running the computation and the result of the computation
* will be also a {@code Try.failure(CancellableException)}.
*
* If during the execution of the computation in a thread, this thread is interrupted for any reason, the result
* of the computation will be a {@code Try.failure(InterruptedException)}.
*
* @param result of the computation
* @see Try
* @see Promise
*/
@Sealed
@HigherKind
public interface Future {
Executor DEFAULT_EXECUTOR = Executors.newCachedThreadPool();
Try await();
Try await(Duration timeout);
void cancel(boolean mayInterruptThread);
boolean isCompleted();
boolean isCancelled();
Future onSuccess(Consumer1 callback);
Future onFailure(Consumer1 callback);
Future onComplete(Consumer1> callback);
Future map(Function1 mapper);
Future flatMap(Function1> mapper);
Future andThen(Future next);
Future filter(Matcher1 matcher);
default Future filterNot(Matcher1 matcher) {
return filter(matcher.negate());
}
Future orElse(Future other);
default T get() {
return getOrElseThrow(NoSuchElementException::new);
}
default T getOrElse(T value) {
return getOrElse(Producer.cons(value));
}
default T getOrElse(Producer value) {
return await().getOrElse(value);
}
default T getOrElseThrow(Producer producer) throws X {
return await().getOrElseThrow(producer);
}
default Throwable getCause() {
return await().getCause();
}
default Future recover(Function1 mapper) {
return fold(mapper, identity());
}
Future recoverWith(Class type, Function1 mapper);
Future fold(Function1 failureMapper, Function1 successMapper);
default CompletableFuture toCompletableFuture() {
CompletableFuture completableFuture = new CompletableFuture<>();
onSuccess(completableFuture::complete);
onFailure(completableFuture::completeExceptionally);
return completableFuture;
}
Promise toPromise();
FutureModule getModule();
static Future success(T value) {
return success(DEFAULT_EXECUTOR, value);
}
static Future success(Executor executor, T value) {
return FutureImpl.sync(executor, Try.success(value));
}
static Future failure(Throwable error) {
return failure(DEFAULT_EXECUTOR, error);
}
static Future failure(Executor executor, Throwable error) {
return FutureImpl.sync(executor, Try.failure(error));
}
static Future from(Callable callable) {
return from(DEFAULT_EXECUTOR, callable);
}
static Future from(Executor executor, Callable callable) {
return async(executor, callable::call);
}
static Future from(java.util.concurrent.Future future) {
return from(DEFAULT_EXECUTOR, future);
}
static Future from(Executor executor, java.util.concurrent.Future future) {
return async(executor, future::get);
}
static Future async(Producer task) {
return async(DEFAULT_EXECUTOR, task);
}
static Future async(Executor executor, Producer task) {
return FutureImpl.async(executor, task.liftTry());
}
static Future exec(CheckedRunnable task) {
return exec(DEFAULT_EXECUTOR, task);
}
static Future exec(Executor executor, CheckedRunnable task) {
return async(executor, () -> { task.run(); return unit(); });
}
static Future delay(Duration timeout, Producer producer) {
return delay(DEFAULT_EXECUTOR, timeout, producer);
}
static Future delay(Executor executor, Duration timeout, Producer producer) {
return sleep(executor, timeout).flatMap(x -> async(executor, producer));
}
static Future sleep(Duration delay) {
return sleep(DEFAULT_EXECUTOR, delay);
}
static Future sleep(Executor executor, Duration delay) {
return FutureImpl.sleep(executor, delay);
}
static Future defer(Producer> producer) {
return defer(DEFAULT_EXECUTOR, producer);
}
static Future defer(Executor executor, Producer> producer) {
return async(executor, producer::get).flatMap(identity());
}
static Future bracket(Future acquire, Function1> use) {
return bracket(DEFAULT_EXECUTOR, acquire, use);
}
static Future bracket(Executor executor, Future acquire, Function1> use) {
return FutureImpl.bracket(executor, acquire, use, AutoCloseable::close);
}
static Future bracket(Future acquire, Function1> use, Consumer1 release) {
return bracket(DEFAULT_EXECUTOR, acquire, use, release);
}
static Future bracket(Executor executor, Future acquire, Function1> use, Consumer1 release) {
return FutureImpl.bracket(executor, acquire, use, release);
}
}
interface FutureModule { }
final class FutureImpl implements Future {
private final Executor executor;
private final Consumer1 propagate;
private final Promise promise;
private final Cancellable cancellable;
private FutureImpl(Executor executor, Consumer2, Cancellable> callback) {
this(executor, callback, x -> {});
}
protected FutureImpl(Executor executor, Consumer2, Cancellable> callback, Consumer1 propagate) {
this.executor = requireNonNull(executor);
this.propagate = requireNonNull(propagate);
this.promise = Promise.make(executor);
this.cancellable = Cancellable.from(promise);
callback.accept(promise, cancellable);
}
@Override
public Future onComplete(Consumer1> consumer) {
promise.onComplete(consumer);
return this;
}
@Override
public Future onSuccess(Consumer1 consumer) {
promise.onSuccess(consumer);
return this;
}
@Override
public Future onFailure(Consumer1 consumer) {
promise.onFailure(consumer);
return this;
}
@Override
public boolean isCompleted() {
return promise.isCompleted();
}
@Override
public boolean isCancelled() {
return cancellable.isCancelled();
}
@Override
public Try await() {
return promise.get();
}
@Override
public Try await(Duration timeout) {
return promise.get(timeout);
}
@Override
public Future map(Function1 mapper) {
return transform(executor, this, value -> value.map(mapper));
}
@Override
public Future flatMap(Function1> mapper) {
return chain(executor, this, value -> value.fold(e -> Future.failure(executor, e), mapper));
}
@Override
public Future andThen(Future next) {
return flatMap(ignore -> next);
}
@Override
public Future filter(Matcher1 matcher) {
return transform(executor, this, value -> value.filter(matcher));
}
@Override
public Future orElse(Future other) {
return chain(executor, this, value -> value.fold(cons(other), t -> Future.success(executor, t)));
}
@Override
public Future recoverWith(Class type, Function1 mapper) {
return transform(executor, this, value -> value.recoverWith(type, mapper));
}
@Override
public Future fold(Function1 failureMapper, Function1 successMapper) {
return transform(executor, this, value -> Try.success(value.fold(failureMapper, successMapper)));
}
@Override
public void cancel(boolean mayInterruptThread) {
try {
cancellable.cancel(mayInterruptThread);
} finally {
propagate.accept(mayInterruptThread);
}
}
@Override
public Promise toPromise() {
return promise;
}
@Override
public FutureModule getModule() {
throw new UnsupportedOperationException();
}
protected static Future sync(Executor executor, Try result) {
requireNonNull(executor);
requireNonNull(result);
return new FutureImpl<>(executor, (promise, cancel) -> promise.tryComplete(result));
}
protected static Future transform(Executor executor, Future current, Function1, Try> mapper) {
requireNonNull(executor);
requireNonNull(current);
requireNonNull(mapper);
return new FutureImpl<>(executor,
(promise, cancellable) ->
current.onComplete(value -> promise.tryComplete(mapper.apply(value))), current::cancel);
}
protected static Future chain(Executor executor, Future current, Function1, Future> mapper) {
requireNonNull(executor);
requireNonNull(current);
requireNonNull(mapper);
return new FutureImpl<>(executor,
(promise, cancellable) ->
current.onComplete(value -> mapper.apply(value).onComplete(promise::tryComplete)), current::cancel);
}
protected static Future async(Executor executor, Producer> producer) {
requireNonNull(executor);
requireNonNull(producer);
return new FutureImpl<>(executor,
(promise, cancellable) ->
executor.execute(() -> {
cancellable.updateThread();
promise.tryComplete(producer.get());
}));
}
protected static Future from(Executor executor, Promise promise) {
requireNonNull(executor);
requireNonNull(promise);
return new FutureImpl<>(executor, (current, cancel) -> promise.onComplete(current::tryComplete));
}
protected static Future sleep(Executor executor, Duration delay) {
requireNonNull(executor);
requireNonNull(delay);
return from(executor, Delayed.sleep(executor, delay));
}
protected static Future bracket(Executor executor, Future acquire, Function1> use, Consumer1 release) {
requireNonNull(executor);
requireNonNull(acquire);
requireNonNull(use);
requireNonNull(release);
return new FutureImpl<>(executor,
(promise, cancellable) ->
acquire.onComplete(
resource -> resource.fold(e -> Future.failure(executor, e), use)
.onComplete(promise::tryComplete)
.onComplete(result -> resource.onSuccess(release))));
}
}
final class Delayed {
private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(0);
protected static Promise sleep(Executor executor, Duration delay) {
return Promise.from(executor, supplyAsync(Unit::unit, delayedExecutor(delay, executor)));
}
private static Executor delayedExecutor(Duration delay, Executor executor) {
return task -> SCHEDULER.schedule(() -> executor.execute(task), delay.toMillis(), TimeUnit.MILLISECONDS);
}
}