mirror of
https://github.com/tommyskeff/futur4j.git
synced 2026-01-17 23:16:01 +00:00
more optimized promise implementation for completed promises
This commit is contained in:
@@ -9,47 +9,29 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.VarHandle;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@SuppressWarnings({"FieldMayBeFinal", "unchecked"})
|
public abstract class AbstractPromise<T, FS, FA> implements Promise<T> {
|
||||||
public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T> {
|
|
||||||
|
|
||||||
private static final VarHandle COMPLETION_HANDLE;
|
|
||||||
private static final VarHandle LISTENERS_HANDLE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
||||||
COMPLETION_HANDLE = lookup.findVarHandle(AbstractPromise.class, "completion", PromiseCompletion.class);
|
|
||||||
LISTENERS_HANDLE = lookup.findVarHandle(AbstractPromise.class, "listeners", Collection.class);
|
|
||||||
} catch (ReflectiveOperationException e) {
|
|
||||||
throw new ExceptionInInitializerError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Sync sync;
|
|
||||||
|
|
||||||
private volatile Collection<PromiseListener<T>> listeners;
|
|
||||||
private volatile PromiseCompletion<T> completion;
|
|
||||||
|
|
||||||
public AbstractPromise() {
|
|
||||||
this.sync = new Sync();
|
|
||||||
this.listeners = Collections.EMPTY_LIST;
|
|
||||||
this.completion = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract @NotNull AbstractPromiseFactory<FS, FA> getFactory();
|
public abstract @NotNull AbstractPromiseFactory<FS, FA> getFactory();
|
||||||
|
|
||||||
private void runCompleter(@NotNull CompletablePromise<?> promise, @NotNull ExceptionalRunnable completer) {
|
protected abstract @NotNull Promise<T> addAnyListener(@NotNull PromiseListener<T> listener);
|
||||||
|
|
||||||
|
protected @NotNull Logger getLogger() {
|
||||||
|
return getFactory().getLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void callListener(@NotNull PromiseListener<T> listener, @NotNull PromiseCompletion<T> cmp) {
|
||||||
|
if (listener instanceof AsyncPromiseListener) {
|
||||||
|
callListenerAsync(listener, cmp);
|
||||||
|
} else {
|
||||||
|
callListenerNow(listener, cmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void runCompleter(@NotNull CompletablePromise<?> promise, @NotNull ExceptionalRunnable completer) {
|
||||||
try {
|
try {
|
||||||
completer.run();
|
completer.run();
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
@@ -60,11 +42,8 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <V> @NotNull Runnable createCompleter(
|
protected <V> @NotNull Runnable createCompleter(T result, @NotNull CompletablePromise<V> promise,
|
||||||
T result,
|
@NotNull ExceptionalFunction<T, V> completer) {
|
||||||
@NotNull CompletablePromise<V> promise,
|
|
||||||
@NotNull ExceptionalFunction<T, V> completer
|
|
||||||
) {
|
|
||||||
return () -> {
|
return () -> {
|
||||||
if (!promise.isCompleted()) {
|
if (!promise.isCompleted()) {
|
||||||
runCompleter(promise, () -> promise.complete(completer.apply(result)));
|
runCompleter(promise, () -> promise.complete(completer.apply(result)));
|
||||||
@@ -72,43 +51,37 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected @NotNull Logger getLogger() {
|
protected <V> @NotNull CompletablePromise<V> createLinked() {
|
||||||
return getFactory().getLogger();
|
CompletablePromise<V> promise = getFactory().unresolved();
|
||||||
|
PromiseUtil.propagateCancel(promise, this);
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void callListenerAsync(PromiseListener<T> listener, PromiseCompletion<T> res) {
|
||||||
public T get() throws InterruptedException, ExecutionException {
|
|
||||||
sync.acquireSharedInterruptibly(1);
|
|
||||||
return joinCompletion();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get(long time, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
|
||||||
boolean success = sync.tryAcquireSharedNanos(1, unit.toNanos(time));
|
|
||||||
if (!success) {
|
|
||||||
throw new TimeoutException("Promise stopped waiting after " + time + " " + unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
return joinCompletion();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T await() {
|
|
||||||
try {
|
try {
|
||||||
sync.acquireSharedInterruptibly(1);
|
getFactory().getAsyncExecutor().run(() -> callListenerNow(listener, res));
|
||||||
} catch (InterruptedException e) {
|
} catch (RejectedExecutionException ignored) {
|
||||||
throw new RuntimeException(e);
|
} catch (Exception e) {
|
||||||
|
getLogger().warn("Exception caught while running promise listener", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
PromiseCompletion<T> completion = Objects.requireNonNull(getCompletion());
|
|
||||||
if (completion.isSuccess()) return completion.getResult();
|
|
||||||
throw new CompletionException(completion.getException());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private T joinCompletion() throws ExecutionException {
|
protected void callListenerNow(PromiseListener<T> listener, PromiseCompletion<T> res) {
|
||||||
PromiseCompletion<T> completion = Objects.requireNonNull(getCompletion());
|
try {
|
||||||
if (completion.isSuccess()) return completion.getResult();
|
listener.handle(res);
|
||||||
throw new ExecutionException(completion.getException());
|
} catch (Error e) {
|
||||||
|
getLogger().error("Error caught in promise listener", e);
|
||||||
|
throw e;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
getLogger().error("Exception caught in promise listener", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void callListenerAsyncLastResort(PromiseListener<T> listener, PromiseCompletion<T> completion) {
|
||||||
|
try {
|
||||||
|
getFactory().getAsyncExecutor().run(() -> callListenerNow(listener, completion));
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -141,33 +114,68 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> @NotNull Promise<V> thenApply(@NotNull ExceptionalFunction<T, V> task) {
|
public <V> @NotNull Promise<V> thenApply(@NotNull ExceptionalFunction<T, V> task) {
|
||||||
CompletablePromise<V> promise = getFactory().unresolved();
|
PromiseCompletion<T> completion = getCompletion();
|
||||||
addDirectListener(
|
if (completion == null) {
|
||||||
res -> createCompleter(res, promise, task).run(),
|
CompletablePromise<V> promise = createLinked();
|
||||||
promise::completeExceptionally
|
addDirectListener(
|
||||||
);
|
res -> createCompleter(res, promise, task).run(),
|
||||||
|
promise::completeExceptionally
|
||||||
|
);
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
return promise;
|
||||||
return promise;
|
} else if (completion.isSuccess()) {
|
||||||
|
try {
|
||||||
|
V result = task.apply(completion.getResult());
|
||||||
|
return getFactory().resolve(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return getFactory().error(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Throwable ex = completion.getException();
|
||||||
|
assert ex != null;
|
||||||
|
return getFactory().error(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> @NotNull Promise<V> thenCompose(@NotNull ExceptionalFunction<T, Promise<V>> task) {
|
public <V> @NotNull Promise<V> thenCompose(@NotNull ExceptionalFunction<T, Promise<V>> task) {
|
||||||
CompletablePromise<V> promise = getFactory().unresolved();
|
PromiseCompletion<T> completion = getCompletion();
|
||||||
thenApply(task).addDirectListener(
|
if (completion == null) {
|
||||||
nestedPromise -> {
|
CompletablePromise<V> promise = createLinked();
|
||||||
if (nestedPromise == null) {
|
thenApply(task).addDirectListener(
|
||||||
promise.complete(null);
|
result -> {
|
||||||
} else {
|
if (result == null) {
|
||||||
PromiseUtil.propagateCompletion(nestedPromise, promise);
|
promise.complete(null);
|
||||||
PromiseUtil.propagateCancel(promise, nestedPromise);
|
} else {
|
||||||
}
|
PromiseUtil.propagateCompletion(result, promise);
|
||||||
},
|
PromiseUtil.propagateCancel(promise, result);
|
||||||
promise::completeExceptionally
|
}
|
||||||
);
|
},
|
||||||
|
promise::completeExceptionally
|
||||||
|
);
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
return promise;
|
||||||
return promise;
|
} else if (completion.isSuccess()) {
|
||||||
|
try {
|
||||||
|
Promise<V> result = task.apply(completion.getResult());
|
||||||
|
if (result == null) {
|
||||||
|
return getFactory().resolve(null);
|
||||||
|
} else if (result.isCompleted()) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
CompletablePromise<V> promise = createLinked();
|
||||||
|
PromiseUtil.propagateCompletion(result, promise);
|
||||||
|
PromiseUtil.propagateCancel(promise, result);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return getFactory().error(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Throwable ex = completion.getException();
|
||||||
|
assert ex != null;
|
||||||
|
return getFactory().error(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -214,7 +222,7 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> @NotNull Promise<V> thenApplySync(@NotNull ExceptionalFunction<T, V> task) {
|
public <V> @NotNull Promise<V> thenApplySync(@NotNull ExceptionalFunction<T, V> task) {
|
||||||
CompletablePromise<V> promise = getFactory().unresolved();
|
CompletablePromise<V> promise = createLinked();
|
||||||
addDirectListener(
|
addDirectListener(
|
||||||
res -> runCompleter(promise, () -> {
|
res -> runCompleter(promise, () -> {
|
||||||
Runnable runnable = createCompleter(res, promise, task);
|
Runnable runnable = createCompleter(res, promise, task);
|
||||||
@@ -224,13 +232,12 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
promise::completeExceptionally
|
promise::completeExceptionally
|
||||||
);
|
);
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> @NotNull Promise<V> thenApplyDelayedSync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit) {
|
public <V> @NotNull Promise<V> thenApplyDelayedSync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit) {
|
||||||
CompletablePromise<V> promise = getFactory().unresolved();
|
CompletablePromise<V> promise = createLinked();
|
||||||
addDirectListener(
|
addDirectListener(
|
||||||
res -> runCompleter(promise, () -> {
|
res -> runCompleter(promise, () -> {
|
||||||
Runnable runnable = createCompleter(res, promise, task);
|
Runnable runnable = createCompleter(res, promise, task);
|
||||||
@@ -240,13 +247,12 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
promise::completeExceptionally
|
promise::completeExceptionally
|
||||||
);
|
);
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> @NotNull Promise<V> thenComposeSync(@NotNull ExceptionalFunction<T, Promise<V>> task) {
|
public <V> @NotNull Promise<V> thenComposeSync(@NotNull ExceptionalFunction<T, Promise<V>> task) {
|
||||||
CompletablePromise<V> promise = getFactory().unresolved();
|
CompletablePromise<V> promise = createLinked();
|
||||||
thenApplySync(task).addDirectListener(
|
thenApplySync(task).addDirectListener(
|
||||||
nestedPromise -> {
|
nestedPromise -> {
|
||||||
if (nestedPromise == null) {
|
if (nestedPromise == null) {
|
||||||
@@ -259,7 +265,6 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
promise::completeExceptionally
|
promise::completeExceptionally
|
||||||
);
|
);
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +312,7 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> @NotNull Promise<V> thenApplyAsync(@NotNull ExceptionalFunction<T, V> task) {
|
public <V> @NotNull Promise<V> thenApplyAsync(@NotNull ExceptionalFunction<T, V> task) {
|
||||||
CompletablePromise<V> promise = getFactory().unresolved();
|
CompletablePromise<V> promise = createLinked();
|
||||||
addDirectListener(
|
addDirectListener(
|
||||||
(res) -> runCompleter(promise, () -> {
|
(res) -> runCompleter(promise, () -> {
|
||||||
Runnable runnable = createCompleter(res, promise, task);
|
Runnable runnable = createCompleter(res, promise, task);
|
||||||
@@ -317,13 +322,12 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
promise::completeExceptionally
|
promise::completeExceptionally
|
||||||
);
|
);
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> @NotNull Promise<V> thenApplyDelayedAsync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit) {
|
public <V> @NotNull Promise<V> thenApplyDelayedAsync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit) {
|
||||||
CompletablePromise<V> promise = getFactory().unresolved();
|
CompletablePromise<V> promise = createLinked();
|
||||||
addDirectListener(
|
addDirectListener(
|
||||||
res -> runCompleter(promise, () -> {
|
res -> runCompleter(promise, () -> {
|
||||||
Runnable runnable = createCompleter(res, promise, task);
|
Runnable runnable = createCompleter(res, promise, task);
|
||||||
@@ -333,13 +337,12 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
promise::completeExceptionally
|
promise::completeExceptionally
|
||||||
);
|
);
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> @NotNull Promise<V> thenComposeAsync(@NotNull ExceptionalFunction<T, Promise<V>> task) {
|
public <V> @NotNull Promise<V> thenComposeAsync(@NotNull ExceptionalFunction<T, Promise<V>> task) {
|
||||||
CompletablePromise<V> promise = getFactory().unresolved();
|
CompletablePromise<V> promise = createLinked();
|
||||||
thenApplyAsync(task).addDirectListener(
|
thenApplyAsync(task).addDirectListener(
|
||||||
nestedPromise -> {
|
nestedPromise -> {
|
||||||
if (nestedPromise == null) {
|
if (nestedPromise == null) {
|
||||||
@@ -352,7 +355,6 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
promise::completeExceptionally
|
promise::completeExceptionally
|
||||||
);
|
);
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,51 +403,6 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NotNull Promise<T> addAnyListener(PromiseListener<T> listener) {
|
|
||||||
Collection<PromiseListener<T>> prev = listeners, next = null;
|
|
||||||
for (boolean haveNext = false; ; ) {
|
|
||||||
if (!haveNext) {
|
|
||||||
next = prev == Collections.EMPTY_LIST ? new ConcurrentLinkedQueue<>() : prev;
|
|
||||||
if (next != null) next.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LISTENERS_HANDLE.weakCompareAndSet(this, prev, next))
|
|
||||||
break;
|
|
||||||
|
|
||||||
haveNext = (prev == (prev = listeners));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next == null) {
|
|
||||||
if (listener instanceof AsyncPromiseListener) {
|
|
||||||
callListenerAsync(listener, Objects.requireNonNull(getCompletion()));
|
|
||||||
} else {
|
|
||||||
callListenerNow(listener, Objects.requireNonNull(getCompletion()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void callListenerAsync(PromiseListener<T> listener, PromiseCompletion<T> res) {
|
|
||||||
try {
|
|
||||||
getFactory().getAsyncExecutor().run(() -> callListenerNow(listener, res));
|
|
||||||
} catch (RejectedExecutionException ignored) {
|
|
||||||
} catch (Exception e) {
|
|
||||||
getLogger().warn("Exception caught while running promise listener", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void callListenerNow(PromiseListener<T> listener, PromiseCompletion<T> res) {
|
|
||||||
try {
|
|
||||||
listener.handle(res);
|
|
||||||
} catch (Error e) {
|
|
||||||
getLogger().error("Error caught in promise listener", e);
|
|
||||||
throw e;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
getLogger().error("Exception caught in promise listener", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Promise<T> onSuccess(@NotNull Consumer<T> listener) {
|
public @NotNull Promise<T> onSuccess(@NotNull Consumer<T> listener) {
|
||||||
return addAsyncListener(listener, null);
|
return addAsyncListener(listener, null);
|
||||||
@@ -489,92 +446,11 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Promise<T> orDefault(@NotNull ExceptionalFunction<Throwable, T> function) {
|
public @NotNull Promise<T> orDefault(@NotNull ExceptionalFunction<Throwable, T> function) {
|
||||||
CompletablePromise<T> promise = getFactory().unresolved();
|
CompletablePromise<T> promise = createLinked();
|
||||||
addDirectListener(promise::complete, e -> {
|
addDirectListener(promise::complete, e -> runCompleter(promise, () -> promise.complete(function.apply(e))));
|
||||||
try {
|
|
||||||
T result = function.apply(e);
|
|
||||||
promise.complete(result);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
promise.completeExceptionally(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
PromiseUtil.propagateCancel(promise, this);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit) {
|
|
||||||
Exception e = new CancellationException("Promise timed out after " + time + " " + unit.toString().toLowerCase());
|
|
||||||
return completeExceptionallyDelayed(e, time, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Promise<T> maxWaitTime(long time, @NotNull TimeUnit unit) {
|
|
||||||
Exception e = new TimeoutException("Promise stopped waiting after " + time + " " + unit.toString().toLowerCase());
|
|
||||||
return completeExceptionallyDelayed(e, time, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Promise<T> completeExceptionallyDelayed(Throwable e, long delay, TimeUnit unit) {
|
|
||||||
runCompleter(this, () -> {
|
|
||||||
FA future = getFactory().getAsyncExecutor().run(() -> completeExceptionally(e), delay, unit);
|
|
||||||
addDirectListener(_ -> getFactory().getAsyncExecutor().cancel(future));
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleCompletion(@NotNull PromiseCompletion<T> cmp) {
|
|
||||||
if (!COMPLETION_HANDLE.compareAndSet(this, null, cmp)) return;
|
|
||||||
sync.releaseShared(1);
|
|
||||||
|
|
||||||
|
|
||||||
Iterator<PromiseListener<T>> iter = ((Iterable<PromiseListener<T>>) LISTENERS_HANDLE.getAndSet(this, null)).iterator();
|
|
||||||
try {
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
PromiseListener<T> listener = iter.next();
|
|
||||||
if (listener instanceof AsyncPromiseListener) {
|
|
||||||
callListenerAsync(listener, cmp);
|
|
||||||
} else {
|
|
||||||
callListenerNow(listener, cmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
iter.forEachRemaining(v -> callListenerAsyncLastResort(v, cmp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void callListenerAsyncLastResort(PromiseListener<T> listener, PromiseCompletion<T> completion) {
|
|
||||||
try {
|
|
||||||
getFactory().getAsyncExecutor().run(() -> callListenerNow(listener, completion));
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancel(@NotNull CancellationException e) {
|
|
||||||
completeExceptionally(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void complete(@Nullable T result) {
|
|
||||||
handleCompletion(new PromiseCompletion<>(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completeExceptionally(@NotNull Throwable result) {
|
|
||||||
handleCompletion(new PromiseCompletion<>(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCompleted() {
|
|
||||||
return completion != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable PromiseCompletion<T> getCompletion() {
|
|
||||||
return completion;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull CompletableFuture<T> toFuture() {
|
public @NotNull CompletableFuture<T> toFuture() {
|
||||||
CompletableFuture<T> future = new CompletableFuture<>();
|
CompletableFuture<T> future = new CompletableFuture<>();
|
||||||
@@ -591,31 +467,4 @@ public abstract class AbstractPromise<T, FS, FA> implements CompletablePromise<T
|
|||||||
private static class DeferredExecutionException extends ExecutionException {
|
private static class DeferredExecutionException extends ExecutionException {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class Sync extends AbstractQueuedSynchronizer {
|
|
||||||
|
|
||||||
private Sync() {
|
|
||||||
setState(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int tryAcquireShared(int acquires) {
|
|
||||||
return getState() == 0 ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean tryReleaseShared(int releases) {
|
|
||||||
int c1, c2;
|
|
||||||
do {
|
|
||||||
c1 = getState();
|
|
||||||
if (c1 == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
c2 = c1 - 1;
|
|
||||||
} while (!compareAndSetState(c1, c2));
|
|
||||||
|
|
||||||
return c2 == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import java.util.*;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionStage;
|
import java.util.concurrent.CompletionStage;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public abstract class AbstractPromiseFactory<FS, FA> implements PromiseFactory {
|
public abstract class AbstractPromiseFactory<FS, FA> implements PromiseFactory {
|
||||||
|
|
||||||
@@ -23,20 +22,6 @@ public abstract class AbstractPromiseFactory<FS, FA> implements PromiseFactory {
|
|||||||
|
|
||||||
public abstract @NotNull PromiseExecutor<FA> getAsyncExecutor();
|
public abstract @NotNull PromiseExecutor<FA> getAsyncExecutor();
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> @NotNull Promise<T> resolve(T value) {
|
|
||||||
CompletablePromise<T> promise = unresolved();
|
|
||||||
promise.complete(value);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> @NotNull Promise<T> error(@NotNull Throwable error) {
|
|
||||||
CompletablePromise<T> promise = unresolved();
|
|
||||||
promise.completeExceptionally(error);
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> @NotNull Promise<T> wrap(@NotNull CompletableFuture<T> future) {
|
public <T> @NotNull Promise<T> wrap(@NotNull CompletableFuture<T> future) {
|
||||||
return wrap(future, future);
|
return wrap(future, future);
|
||||||
@@ -112,14 +97,4 @@ public abstract class AbstractPromiseFactory<FS, FA> implements PromiseFactory {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <V> @NotNull Promise<V> race(@NotNull Iterable<Promise<V>> promises, boolean cancelLosers) {
|
|
||||||
return race(promises.iterator(), cancelLosers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <V> @NotNull Promise<V> race(@NotNull Stream<Promise<V>> promises, boolean cancelLosers) {
|
|
||||||
return race(promises.iterator(), cancelLosers);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,4 @@ package dev.tommyjs.futur.promise;
|
|||||||
* executed asynchronously by the {@link PromiseFactory} that created the completed promise.
|
* executed asynchronously by the {@link PromiseFactory} that created the completed promise.
|
||||||
*/
|
*/
|
||||||
public interface AsyncPromiseListener<T> extends PromiseListener<T> {
|
public interface AsyncPromiseListener<T> extends PromiseListener<T> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package dev.tommyjs.futur.promise;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.VarHandle;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
||||||
|
|
||||||
|
@SuppressWarnings({"FieldMayBeFinal"})
|
||||||
|
public abstract class BasePromise<T, FS, FA> extends AbstractPromise<T, FS, FA> implements CompletablePromise<T> {
|
||||||
|
|
||||||
|
private static final VarHandle COMPLETION_HANDLE;
|
||||||
|
private static final VarHandle LISTENERS_HANDLE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
COMPLETION_HANDLE = lookup.findVarHandle(BasePromise.class, "completion", PromiseCompletion.class);
|
||||||
|
LISTENERS_HANDLE = lookup.findVarHandle(BasePromise.class, "listeners", Collection.class);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new ExceptionInInitializerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Sync sync;
|
||||||
|
|
||||||
|
private volatile PromiseCompletion<T> completion;
|
||||||
|
|
||||||
|
@SuppressWarnings("FieldMayBeFinal")
|
||||||
|
private volatile Collection<PromiseListener<T>> listeners;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public BasePromise() {
|
||||||
|
this.sync = new Sync();
|
||||||
|
this.completion = null;
|
||||||
|
this.listeners = Collections.EMPTY_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected T joinCompletion() throws ExecutionException {
|
||||||
|
PromiseCompletion<T> completion = Objects.requireNonNull(getCompletion());
|
||||||
|
if (completion.isSuccess()) return completion.getResult();
|
||||||
|
throw new ExecutionException(completion.getException());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleCompletion(@NotNull PromiseCompletion<T> cmp) {
|
||||||
|
if (!COMPLETION_HANDLE.compareAndSet(this, null, cmp)) return;
|
||||||
|
sync.releaseShared(1);
|
||||||
|
callListeners(cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Promise<T> completeExceptionallyDelayed(Throwable e, long delay, TimeUnit unit) {
|
||||||
|
runCompleter(this, () -> {
|
||||||
|
FA future = getFactory().getAsyncExecutor().run(() -> completeExceptionally(e), delay, unit);
|
||||||
|
addDirectListener(_ -> getFactory().getAsyncExecutor().cancel(future));
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void callListeners(@NotNull PromiseCompletion<T> cmp) {
|
||||||
|
Iterator<PromiseListener<T>> iter = ((Iterable<PromiseListener<T>>) LISTENERS_HANDLE.getAndSet(this, null)).iterator();
|
||||||
|
try {
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
callListener(iter.next(), cmp);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
iter.forEachRemaining(v -> callListenerAsyncLastResort(v, cmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull Promise<T> addAnyListener(@NotNull PromiseListener<T> listener) {
|
||||||
|
Collection<PromiseListener<T>> prev = listeners, next = null;
|
||||||
|
for (boolean haveNext = false; ; ) {
|
||||||
|
if (!haveNext) {
|
||||||
|
next = prev == Collections.EMPTY_LIST ? new ConcurrentLinkedQueue<>() : prev;
|
||||||
|
if (next != null) next.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LISTENERS_HANDLE.weakCompareAndSet(this, prev, next))
|
||||||
|
break;
|
||||||
|
|
||||||
|
haveNext = (prev == (prev = listeners));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next == null) {
|
||||||
|
callListener(listener, Objects.requireNonNull(getCompletion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() throws InterruptedException, ExecutionException {
|
||||||
|
sync.acquireSharedInterruptibly(1);
|
||||||
|
return joinCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get(long time, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
boolean success = sync.tryAcquireSharedNanos(1, unit.toNanos(time));
|
||||||
|
if (!success) {
|
||||||
|
throw new TimeoutException("Promise stopped waiting after " + time + " " + unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return joinCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T await() {
|
||||||
|
try {
|
||||||
|
sync.acquireSharedInterruptibly(1);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
PromiseCompletion<T> completion = Objects.requireNonNull(getCompletion());
|
||||||
|
if (completion.isSuccess()) return completion.getResult();
|
||||||
|
throw new CompletionException(completion.getException());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit) {
|
||||||
|
Exception e = new CancellationException("Promise timed out after " + time + " " + unit.toString().toLowerCase());
|
||||||
|
return completeExceptionallyDelayed(e, time, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Promise<T> maxWaitTime(long time, @NotNull TimeUnit unit) {
|
||||||
|
Exception e = new TimeoutException("Promise stopped waiting after " + time + " " + unit.toString().toLowerCase());
|
||||||
|
return completeExceptionallyDelayed(e, time, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(@NotNull CancellationException e) {
|
||||||
|
completeExceptionally(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void complete(@Nullable T result) {
|
||||||
|
handleCompletion(new PromiseCompletion<>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completeExceptionally(@NotNull Throwable result) {
|
||||||
|
handleCompletion(new PromiseCompletion<>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return completion != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable PromiseCompletion<T> getCompletion() {
|
||||||
|
return completion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Sync extends AbstractQueuedSynchronizer {
|
||||||
|
|
||||||
|
private Sync() {
|
||||||
|
setState(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int tryAcquireShared(int acquires) {
|
||||||
|
return getState() == 0 ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean tryReleaseShared(int releases) {
|
||||||
|
int c1, c2;
|
||||||
|
do {
|
||||||
|
c1 = getState();
|
||||||
|
if (c1 == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
c2 = c1 - 1;
|
||||||
|
} while (!compareAndSetState(c1, c2));
|
||||||
|
|
||||||
|
return c2 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package dev.tommyjs.futur.promise;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public abstract class CompletedPromise<T, FS, FA> extends AbstractPromise<T, FS, FA> {
|
||||||
|
|
||||||
|
private static final PromiseCompletion<?> EMPTY = new PromiseCompletion<>();
|
||||||
|
|
||||||
|
private final @NotNull PromiseCompletion<T> completion;
|
||||||
|
|
||||||
|
public CompletedPromise(@NotNull PromiseCompletion<T> completion) {
|
||||||
|
this.completion = completion;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public CompletedPromise() {
|
||||||
|
this((PromiseCompletion<T>) EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull Promise<T> addAnyListener(@NotNull PromiseListener<T> listener) {
|
||||||
|
callListener(listener, completion);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Promise<T> maxWaitTime(long time, @NotNull TimeUnit unit) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(@NotNull CancellationException exception) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get(long timeout, @NotNull TimeUnit unit) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T await() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull PromiseCompletion<T> getCompletion() {
|
||||||
|
return completion;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -70,9 +70,7 @@ public interface PromiseFactory {
|
|||||||
* @return the new promise
|
* @return the new promise
|
||||||
* @apiNote This method is often useful for starting promise chains.
|
* @apiNote This method is often useful for starting promise chains.
|
||||||
*/
|
*/
|
||||||
default @NotNull Promise<Void> start() {
|
@NotNull Promise<Void> start();
|
||||||
return resolve(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new promise, completed exceptionally with the given error.
|
* Creates a new promise, completed exceptionally with the given error.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dev.tommyjs.futur.promise;
|
|||||||
|
|
||||||
import dev.tommyjs.futur.executor.PromiseExecutor;
|
import dev.tommyjs.futur.executor.PromiseExecutor;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
public class PromiseFactoryImpl<FS, FA> extends AbstractPromiseFactory<FS, FA> {
|
public class PromiseFactoryImpl<FS, FA> extends AbstractPromiseFactory<FS, FA> {
|
||||||
@@ -25,6 +26,21 @@ public class PromiseFactoryImpl<FS, FA> extends AbstractPromiseFactory<FS, FA> {
|
|||||||
return new PromiseImpl<>();
|
return new PromiseImpl<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull <T> Promise<T> resolve(T value) {
|
||||||
|
return new CompletedPromiseImpl<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Promise<Void> start() {
|
||||||
|
return new CompletedPromiseImpl<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull <T> Promise<T> error(@NotNull Throwable error) {
|
||||||
|
return new CompletedPromiseImpl<>(error);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Logger getLogger() {
|
public @NotNull Logger getLogger() {
|
||||||
return logger;
|
return logger;
|
||||||
@@ -40,7 +56,28 @@ public class PromiseFactoryImpl<FS, FA> extends AbstractPromiseFactory<FS, FA> {
|
|||||||
return asyncExecutor;
|
return asyncExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PromiseImpl<T> extends AbstractPromise<T, FS, FA> {
|
private class PromiseImpl<T> extends BasePromise<T, FS, FA> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull AbstractPromiseFactory<FS, FA> getFactory() {
|
||||||
|
return PromiseFactoryImpl.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CompletedPromiseImpl<T> extends CompletedPromise<T, FS, FA> {
|
||||||
|
|
||||||
|
public CompletedPromiseImpl(@Nullable T result) {
|
||||||
|
super(new PromiseCompletion<>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletedPromiseImpl(@NotNull Throwable exception) {
|
||||||
|
super(new PromiseCompletion<>(exception));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletedPromiseImpl() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull AbstractPromiseFactory<FS, FA> getFactory() {
|
public @NotNull AbstractPromiseFactory<FS, FA> getFactory() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dev.tommyjs.futur;
|
package dev.tommyjs.futur;
|
||||||
|
|
||||||
import dev.tommyjs.futur.promise.CompletablePromise;
|
import dev.tommyjs.futur.promise.CompletablePromise;
|
||||||
|
import dev.tommyjs.futur.promise.CompletedPromise;
|
||||||
import dev.tommyjs.futur.promise.Promise;
|
import dev.tommyjs.futur.promise.Promise;
|
||||||
import dev.tommyjs.futur.promise.PromiseFactory;
|
import dev.tommyjs.futur.promise.PromiseFactory;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -17,7 +18,7 @@ import java.util.stream.Stream;
|
|||||||
public final class PromiseTests {
|
public final class PromiseTests {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(PromiseTests.class);
|
private final Logger logger = LoggerFactory.getLogger(PromiseTests.class);
|
||||||
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
|
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(6);
|
||||||
private final PromiseFactory promises = PromiseFactory.of(logger, executor);
|
private final PromiseFactory promises = PromiseFactory.of(logger, executor);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -229,4 +230,17 @@ public final class PromiseTests {
|
|||||||
assert res.get(3) == 6;
|
assert res.get(3) == 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImmediate1() {
|
||||||
|
var promise = promises.start().thenSupply(() -> 10);
|
||||||
|
assert promise.isCompleted() && promise instanceof CompletedPromise<?,?,?>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImmediate2() {
|
||||||
|
var resolved = promises.resolve(10);
|
||||||
|
var promise = promises.start().thenCompose(_ -> resolved);
|
||||||
|
assert promise.isCompleted() && promise instanceof CompletedPromise<?,?,?>;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user