optimizations, more comfortable PromiseFactory api and support virtual threaded executors

This commit is contained in:
WhatCats
2025-01-06 14:06:39 +01:00
parent 18d334a530
commit 9e392c91ba
39 changed files with 1205 additions and 833 deletions

View File

@@ -0,0 +1,48 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
public class CompletionJoiner extends PromiseJoiner<Promise<?>, Void, Void, List<PromiseCompletion<?>>> {
private final ConcurrentResultArray<PromiseCompletion<?>> results;
public CompletionJoiner(
@NotNull PromiseFactory factory,
@NotNull Iterator<Promise<?>> promises,
int expectedSize, boolean link
) {
super(factory);
results = new ConcurrentResultArray<>(expectedSize);
join(promises, link);
}
@Override
protected Void getKey(Promise<?> value) {
return null;
}
@Override
protected @NotNull Promise<Void> getPromise(Promise<?> value) {
//noinspection unchecked
return (Promise<Void>) value;
}
@Override
protected @Nullable Throwable onFinish(int index, Void key, @NotNull PromiseCompletion<Void> res) {
results.set(index, res);
return null;
}
@Override
protected List<PromiseCompletion<?>> getResult() {
return results.toList();
}
}

View File

@@ -0,0 +1,32 @@
package dev.tommyjs.futur.joiner;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
class ConcurrentResultArray<T> {
private final AtomicReference<T[]> ref;
public ConcurrentResultArray(int expectedSize) {
//noinspection unchecked
this.ref = new AtomicReference<>((T[]) new Object[expectedSize]);
}
public void set(int index, T element) {
ref.updateAndGet(array -> {
if (array.length <= index)
return Arrays.copyOf(array, index + 6);
array[index] = element;
return array;
});
}
public @NotNull List<T> toList() {
return Arrays.asList(ref.get());
}
}

View File

@@ -0,0 +1,60 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
public class MappedResultJoiner<K, V> extends PromiseJoiner<Map.Entry<K, Promise<V>>, K, V, Map<K, V>> {
private final @Nullable BiConsumer<K, Throwable> exceptionHandler;
private final @NotNull ConcurrentResultArray<Map.Entry<K, V>> results;
public MappedResultJoiner(
@NotNull PromiseFactory factory,
@NotNull Iterator<Map.Entry<K, Promise<V>>> promises,
@Nullable BiConsumer<K, Throwable> exceptionHandler,
int expectedSize, boolean link
) {
super(factory);
this.exceptionHandler = exceptionHandler;
this.results = new ConcurrentResultArray<>(expectedSize);
join(promises, link);
}
@Override
protected K getKey(Map.Entry<K, Promise<V>> entry) {
return entry.getKey();
}
@Override
protected @NotNull Promise<V> getPromise(Map.Entry<K, Promise<V>> entry) {
return entry.getValue();
}
@Override
protected @Nullable Throwable onFinish(int index, K key, @NotNull PromiseCompletion<V> res) {
if (res.isError()) {
if (exceptionHandler == null) return res.getException();
exceptionHandler.accept(key, res.getException());
}
results.set(index, new AbstractMap.SimpleImmutableEntry<>(key, res.getResult()));
return null;
}
@Override
protected Map<K, V> getResult() {
List<Map.Entry<K, V>> list = results.toList();
Map<K, V> map = new HashMap<>(list.size());
for (Map.Entry<K, V> entry : list) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
}

View File

@@ -0,0 +1,66 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
abstract class PromiseJoiner<V, K, T, R> {
private final CompletablePromise<R> joined;
protected PromiseJoiner(@NotNull PromiseFactory factory) {
this.joined = factory.unresolved();
}
public @NotNull Promise<R> joined() {
return joined;
}
protected abstract K getKey(V value);
protected abstract @NotNull Promise<T> getPromise(V value);
protected abstract @Nullable Throwable onFinish(int index, K key, @NotNull PromiseCompletion<T> completion);
protected abstract R getResult();
protected void join(@NotNull Iterator<V> promises, boolean link) {
AtomicBoolean waiting = new AtomicBoolean();
AtomicInteger count = new AtomicInteger();
int i = 0;
do {
V value = promises.next();
Promise<T> p = getPromise(value);
if (link) {
AbstractPromise.cancelOnFinish(p, joined);
}
if (!joined.isCompleted()) {
count.incrementAndGet();
K key = getKey(value);
int index = i++;
p.addListener((res) -> {
Throwable e = onFinish(index, key, res);
if (e != null) {
joined.completeExceptionally(e);
} else if (count.decrementAndGet() == 0 && waiting.get()) {
joined.complete(getResult());
}
});
}
} while (promises.hasNext());
count.updateAndGet((v) -> {
if (v == 0) joined.complete(getResult());
else waiting.set(true);
return v;
});
}
}

View File

@@ -0,0 +1,56 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
public class ResultJoiner<T> extends PromiseJoiner<Promise<T>, Void, T, List<T>> {
private final @Nullable BiConsumer<Integer, Throwable> exceptionHandler;
private final ConcurrentResultArray<T> results;
public ResultJoiner(
@NotNull PromiseFactory factory,
@NotNull Iterator<Promise<T>> promises,
@Nullable BiConsumer<Integer, Throwable> exceptionHandler,
int expectedSize, boolean link
) {
super(factory);
this.exceptionHandler = exceptionHandler;
this.results = new ConcurrentResultArray<>(expectedSize);
join(promises, link);
}
@Override
protected Void getKey(Promise<T> value) {
return null;
}
@Override
protected @NotNull Promise<T> getPromise(Promise<T> value) {
return value;
}
@Override
protected @Nullable Throwable onFinish(int index, Void key, @NotNull PromiseCompletion<T> res) {
if (res.isError()) {
if (exceptionHandler == null) return res.getException();
exceptionHandler.accept(index, res.getException());
}
results.set(index, res.getResult());
return null;
}
@Override
protected List<T> getResult() {
return results.toList();
}
}

View File

@@ -0,0 +1,39 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
public class VoidJoiner extends PromiseJoiner<Promise<?>, Void, Void, Void> {
public VoidJoiner(@NotNull PromiseFactory factory, @NotNull Iterator<Promise<?>> promises, boolean link) {
super(factory);
join(promises, link);
}
@Override
protected Void getKey(Promise<?> value) {
return null;
}
@Override
protected @NotNull Promise<Void> getPromise(Promise<?> value) {
//noinspection unchecked
return (Promise<Void>) value;
}
@Override
protected @Nullable Throwable onFinish(int index, Void key, @NotNull PromiseCompletion<Void> completion) {
return completion.getException();
}
@Override
protected Void getResult() {
return null;
}
}