com.github.tonivade.purefun.concurrent.Promise Maven / Gradle / Ivy
/*
* Copyright (c) 2018-2019, Antonio Gabriel Muñoz Conejo
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.purefun.concurrent;
import static java.util.Objects.isNull;
import static java.util.Objects.requireNonNull;
import java.time.Duration;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import com.github.tonivade.purefun.Consumer1;
import com.github.tonivade.purefun.type.Option;
import com.github.tonivade.purefun.type.Try;
public interface Promise {
boolean tryComplete(Try value);
default Promise complete(Try 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));
}
default Future toFuture() {
return toFuture(Future.DEFAULT_EXECUTOR);
}
default Future toFuture(Executor executor) {
return FutureImpl.from(executor, this);
}
Promise onComplete(Consumer1> consumer);
default Promise onSuccess(Consumer1 consumer) {
return onComplete(value -> value.onSuccess(consumer));
}
default Promise onFailure(Consumer1 consumer) {
return onComplete(value -> value.onFailure(consumer));
}
Try get();
Try get(Duration timeout);
boolean isCompleted();
static Promise make() {
return new PromiseImpl<>();
}
}
final class PromiseImpl implements Promise {
private final State state = new State();
private final Queue>> consumers = new LinkedList<>();
private final AtomicReference> reference = new AtomicReference<>();
@Override
public boolean tryComplete(Try value) {
if (isEmpty()) {
synchronized (state) {
if (isEmpty()) {
state.completed = true;
setValue(value);
state.notifyAll();
return true;
}
}
}
return false;
}
@Override
public Try get() {
if (isEmpty()) {
synchronized (state) {
if (isEmpty()) {
try {
state.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Try.failure(e);
}
}
}
}
return requireNonNull(reference.get());
}
@Override
public Try get(Duration timeout) {
if (isEmpty()) {
synchronized (state) {
if (isEmpty()) {
try {
state.wait(timeout.toMillis());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Try.failure(e);
}
}
}
}
return Option.of(reference::get).getOrElse(Try.failure(new TimeoutException()));
}
@Override
public boolean isCompleted() {
synchronized (state) {
return state.completed;
}
}
@Override
public Promise onComplete(Consumer1> consumer) {
current(consumer).ifPresent(consumer);
return this;
}
private Option> current(Consumer1> consumer) {
Try current = reference.get();
if (isNull(current)) {
synchronized (state) {
current = reference.get();
if (isNull(current)) {
consumers.add(consumer);
}
}
}
return Option.of(current);
}
private void setValue(Try value) {
reference.set(value);
consumers.forEach(consumer -> consumer.accept(value));
}
private boolean isEmpty() {
return isNull(reference.get());
}
private static final class State {
private boolean completed = false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy