com.github.tonivade.purefun.concurrent.Promise Maven / Gradle / Ivy
/*
* Copyright (c) 2018-2021, 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.Precondition.checkNonNull;
import java.time.Duration;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import com.github.tonivade.purefun.Bindable;
import com.github.tonivade.purefun.CheckedRunnable;
import com.github.tonivade.purefun.Consumer1;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.HigherKind;
import com.github.tonivade.purefun.Kind;
import com.github.tonivade.purefun.Unit;
import com.github.tonivade.purefun.type.Option;
import com.github.tonivade.purefun.type.Try;
import com.github.tonivade.purefun.type.TryOf;
@HigherKind(sealed = true)
public interface Promise extends PromiseOf, Bindable {
boolean tryComplete(Try extends T> value);
default Promise cancel() {
return failed(new CancellationException());
}
default Promise complete(Try extends T> value) {
if (tryComplete(value)) {
return this;
} else throw new IllegalStateException("promise already completed");
}
default Promise succeeded(T value) {
return complete(Try.success(value));
}
default Promise failed(Throwable error) {
return complete(Try.failure(error));
}
Promise onComplete(Consumer1 super Try extends T>> consumer);
default Promise onSuccess(Consumer1 super T> consumer) {
return onComplete(value -> value.onSuccess(consumer));
}
default Promise onFailure(Consumer1 super Throwable> consumer) {
return onComplete(value -> value.onFailure(consumer));
}
@Override
Promise map(Function1 super T, ? extends R> mapper);
@Override
default Promise andThen(Kind next) {
return PromiseOf.narrowK(Bindable.super.andThen(next));
}
@Override
Promise flatMap(Function1 super T, ? extends Kind> mapper);
default Promise then(Consumer1 super T> next) {
return map(next.asFunction());
}
default Promise thenRun(CheckedRunnable next) {
return map(next.asProducer().asFunction());
}
Try await();
Try await(Duration timeout);
boolean isCompleted();
static Promise make() {
return make(Future.DEFAULT_EXECUTOR);
}
static Promise make(Executor executor) {
return new PromiseImpl<>(executor);
}
static Promise from(CompletableFuture extends T> future) {
return from(Future.DEFAULT_EXECUTOR, future);
}
static Promise from(Executor executor, CompletableFuture extends T> future) {
Promise promise = make(executor);
future.whenCompleteAsync(
(value, error) -> promise.tryComplete(
value != null ? Try.success(value) : Try.failure(error)), executor);
return promise;
}
}
final class PromiseImpl implements SealedPromise {
private final State state = new State();
private final Queue>> consumers = new LinkedList<>();
private final AtomicReference> reference = new AtomicReference<>();
private final Executor executor;
PromiseImpl(Executor executor) {
this.executor = checkNonNull(executor);
}
@Override
public boolean tryComplete(Try extends T> value) {
if (isEmpty()) {
synchronized (state) {
if (isEmpty()) {
state.completed = true;
setValue(value);
state.notifyAll();
return true;
}
}
}
return false;
}
@Override
public Try await() {
if (isEmpty()) {
try {
synchronized (state) {
while (!state.completed) {
state.wait();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Try.failure(e);
}
}
return TryOf.narrowK(checkNonNull(reference.get()));
}
@Override
public Try await(Duration timeout) {
if (isEmpty()) {
try {
synchronized (state) {
if (!state.completed) {
state.wait(timeout.toMillis());
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Try.failure(e);
}
}
Option> option = Option.of(reference::get).map(TryOf::narrowK);
return option.getOrElse(Try.failure(new TimeoutException()));
}
@Override
public boolean isCompleted() {
synchronized (state) {
return state.completed;
}
}
@Override
public Promise onComplete(Consumer1 super Try extends T>> consumer) {
current(consumer).ifPresent(consumer);
return this;
}
@Override
public Promise map(Function1 super T, ? extends R> mapper) {
Promise other = new PromiseImpl<>(executor);
onComplete(value -> other.tryComplete(value.map(mapper)));
return other;
}
@Override
public Promise flatMap(Function1 super T, ? extends Kind> mapper) {
Promise other = new PromiseImpl<>(executor);
onComplete(value -> {
Try> map = value.map(mapper.andThen(PromiseOf::narrowK));
map.fold(error -> other.tryComplete(Try.failure(error)), next -> next.onComplete(other::tryComplete));
});
return other;
}
private Option> current(Consumer1 super Try extends T>> consumer) {
Try extends T> current = reference.get();
if (current == null) {
synchronized (state) {
current = reference.get();
if (current == null) {
consumers.add(consumer);
}
}
}
return Option.of(TryOf.narrowK(current));
}
private void setValue(Try extends T> value) {
reference.set(value);
consumers.forEach(consumer -> submit(value, consumer));
}
private void submit(Try extends T> value, Consumer1 super Try extends T>> consumer) {
executor.execute(() -> consumer.accept(value));
}
private boolean isEmpty() {
return reference.get() == null;
}
private static final class State {
private boolean completed = false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy