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

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); } }