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

com.github.tonivade.purefun.concurrent.Future Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2018-2024, Antonio Gabriel Muñoz Conejo 
 * Distributed under the terms of the MIT License
 */
package com.github.tonivade.purefun.concurrent;

import static com.github.tonivade.purefun.core.Function1.cons;
import static com.github.tonivade.purefun.core.Function1.identity;
import static com.github.tonivade.purefun.core.Precondition.checkNonNull;
import static java.util.concurrent.CompletableFuture.supplyAsync;

import java.time.Duration;
import java.util.UUID;
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 com.github.tonivade.purefun.HigherKind;
import com.github.tonivade.purefun.Kind;
import com.github.tonivade.purefun.core.Bindable;
import com.github.tonivade.purefun.core.CheckedRunnable;
import com.github.tonivade.purefun.core.Consumer1;
import com.github.tonivade.purefun.core.Function1;
import com.github.tonivade.purefun.core.Function2;
import com.github.tonivade.purefun.core.Matcher1;
import com.github.tonivade.purefun.core.Producer;
import com.github.tonivade.purefun.core.Tuple2;
import com.github.tonivade.purefun.core.Unit;
import com.github.tonivade.purefun.data.ImmutableList;
import com.github.tonivade.purefun.data.Sequence;
import com.github.tonivade.purefun.type.Try;
import com.github.tonivade.purefun.type.TryOf;

/**
 * 

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.task(computation): returns a future that eventually will execute the given computation.
  • *
  • Future.async(consumer): returns a future that eventually will consume the result of a 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.later(computation): returns a future that eventually will execute the given computation that returns another value.
  • *
  • 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 cancellable 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 */ @HigherKind public sealed interface Future extends FutureOf, Bindable, T> { Executor DEFAULT_EXECUTOR = Executors.newVirtualThreadPerTaskExecutor(); 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); @Override Future map(Function1 mapper); Future mapError(Function1 mapper); @Override Future flatMap(Function1, ? extends R>> mapper); Future andThen(Future next); Future ap(Future> apply); Future filter(Matcher1 matcher); default Future filterNot(Matcher1 matcher) { return filter(matcher.negate()); } Future orElse(Future other); default T get() { return getOrElseThrow(); } default T getOrElse(T value) { return getOrElse(Producer.cons(value)); } default T getOrElse(Producer value) { return await().getOrElse(value); } default T getOrElseThrow() { return await().getOrElseThrow(); } 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(); 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 task(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 task(executor, future::get); } static Future from(CompletableFuture future) { return from(DEFAULT_EXECUTOR, future); } static Future from(Executor executor, CompletableFuture future) { return from(executor, Promise.from(future)); } static Future from(Promise promise) { return from(DEFAULT_EXECUTOR, promise); } static Future from(Executor executor, Promise promise) { return FutureImpl.from(executor, promise); } static Future task(Producer task) { return task(DEFAULT_EXECUTOR, task); } static Future task(Executor executor, Producer task) { return FutureImpl.task(executor, task.liftTry()); } static Future exec(CheckedRunnable task) { return exec(DEFAULT_EXECUTOR, task); } static Future exec(Executor executor, CheckedRunnable task) { return task(executor, task.asProducer()); } 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(ignore -> task(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 later(Producer producer) { return later(DEFAULT_EXECUTOR, producer); } static Future later(Executor executor, Producer producer) { return task(executor, producer); } 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); } // TODO static Future> traverse(Sequence> sequence) { return sequence.foldLeft(success(ImmutableList.empty()), (Future> xs, Future a) -> map2(xs, a, Sequence::append)); } static Future map2(Future fa, Future fb, Function2 mapper) { return fb.ap(fa.map(mapper.curried())); } static Future> tuple(Future fa, Future fb) { return map2(fa, fb, Tuple2::of); } static Future async(Consumer1>> consumer) { return async(DEFAULT_EXECUTOR, consumer); } static Future async(Executor executor, Consumer1>> consumer) { return FutureImpl.async(executor, consumer); } } final class FutureImpl implements Future { private final Executor executor; private final Propagate propagate; private final Promise promise; private final Cancellable cancellable; private final UUID uuid; private FutureImpl(Executor executor, Callback callback) { this(executor, callback, Propagate.noop()); } private FutureImpl(Executor executor, Callback callback, Propagate propagate) { this.uuid = UUID.randomUUID(); this.executor = checkNonNull(executor); this.propagate = checkNonNull(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.await(); } @Override public Try await(Duration timeout) { return promise.await(timeout); } @Override public Future map(Function1 mapper) { return transform(value -> value.map(mapper)); } @Override public Future mapError(Function1 mapper) { return transform(value -> value.mapError(mapper)); } @Override public Future flatMap(Function1, ? extends R>> mapper) { return chain(value -> value.fold(e -> Future.failure(executor, e), mapper)); } @Override public Future andThen(Future next) { return flatMap(ignore -> next); } @Override public Future ap(Future> apply) { checkNonNull(apply); return new FutureImpl<>(executor, (p, c) -> promise.onComplete(try1 -> apply.onComplete( try2 -> p.tryComplete(Try.map2(try2, try1, Function1::apply)))), this::cancel); } @Override public Future filter(Matcher1 matcher) { return transform(value -> value.filter(matcher)); } @Override public Future orElse(Future other) { return chain(value -> value.fold(cons(other), t -> Future.success(executor, t))); } @Override public Future recoverWith(Class type, Function1 mapper) { return transform(value -> { Try try1 = TryOf.toTry(value); return try1.recoverWith(type, mapper); }); } @Override public Future fold( Function1 failureMapper, Function1 successMapper) { return transform(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 String toString() { return "Future(" + uuid + ')'; } private Future transform(Function1, ? extends Try> mapper) { checkNonNull(mapper); return new FutureImpl<>(executor, (p, c) -> promise.onComplete(value -> p.tryComplete(mapper.apply(value))), this::cancel); } private Future chain(Function1, ? extends Kind, ? extends R>> mapper) { checkNonNull(executor); checkNonNull(mapper); return new FutureImpl<>(executor, (p, c) -> promise.onComplete(value -> mapper.andThen(FutureOf::toFuture).apply(value).onComplete(p::tryComplete)), this::cancel); } static Future sync(Executor executor, Try result) { checkNonNull(executor); checkNonNull(result); return new FutureImpl<>(executor, (p, c) -> p.tryComplete(result)); } static Future task(Executor executor, Producer> producer) { checkNonNull(executor); checkNonNull(producer); return new FutureImpl<>(executor, (p, c) -> executor.execute(() -> { c.updateThread(); p.tryComplete(producer.get()); })); } static Future async(Executor executor, Consumer1>> consumer) { checkNonNull(executor); checkNonNull(consumer); return new FutureImpl<>(executor, (p, c) -> Future.later(executor, () -> { c.updateThread(); return consumer.asFunction().apply(p::tryComplete); })); } static Future from(Executor executor, Promise promise) { checkNonNull(executor); checkNonNull(promise); return new FutureImpl<>(executor, (p, c) -> promise.onComplete(p::tryComplete)); } static Future sleep(Executor executor, Duration delay) { checkNonNull(executor); checkNonNull(delay); return from(executor, FutureModule.sleep(executor, delay)); } static Future bracket(Executor executor, Future acquire, Function1> use, Consumer1 release) { checkNonNull(executor); checkNonNull(acquire); checkNonNull(use); checkNonNull(release); return new FutureImpl<>(executor, (p, c) -> acquire.onComplete( resource -> resource.fold(e -> Future.failure(executor, e), use) .onComplete(p::tryComplete) .onComplete(result -> resource.onSuccess(release)))); } } interface FutureModule { ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(0); static Promise sleep(Executor executor, Duration delay) { return Promise.from(executor, supplyAsync(Unit::unit, delayedExecutor(delay, executor))); } static Executor delayedExecutor(Duration delay, Executor executor) { return task -> SCHEDULER.schedule(() -> executor.execute(task), delay.toMillis(), TimeUnit.MILLISECONDS); } } interface Callback { void accept(Promise promise, Cancellable cancellable); } interface Propagate { void accept(boolean mayInterruptThread); static Propagate noop() { return mayInterruptThread -> {}; } }