currently refactoring a lot

This commit is contained in:
kento2 2026-07-04 16:29:05 +02:00
parent a32805a623
commit 700b5d5d10
20 changed files with 406 additions and 292 deletions

View file

@ -1,13 +1,15 @@
package de.kentoj.scrow.bukkit.scheduler;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public interface Task<T> {
CompletableFuture<T> asFuture();
CompletableFuture<T> toFuture();
default Task<?> runSync(Runnable task) {
return mapSync(__ -> {
@ -48,4 +50,17 @@ public interface Task<T> {
}
<S> Task<S> mapAsync(Function<T, S> mapper);
Task<T> yieldIf(Predicate<T> precondition);
default T await() {
try {
return toFuture().get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -1,8 +1,10 @@
package de.kentoj.scrow.bukkit.scheduler;
import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.ScrowAPI;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.Bukkit;
import java.util.concurrent.CompletableFuture;
@ -10,47 +12,85 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.*;
/**
* A Task runs entirely async, when sync is used, a subtask is run on the main thread and the async thread
* blocks until the subtask returns.
*
* @param <T> type of computed result
*/
@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class TaskImpl<T> implements Task<T> {
private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
private final Supplier<T> compute;
private final Supplier<TaskState<T>> compute;
private TaskState<T> cachedResult = null;
@Override
public CompletableFuture<T> asFuture() {
return CompletableFuture.supplyAsync(compute, EXECUTOR);
public CompletableFuture<T> toFuture() {
return CompletableFuture.supplyAsync(() -> state().getValue(), EXECUTOR);
}
/**
* Maps the last result on the main thread
*/
public <S> Task<S> mapSync(Function<T, S> mapper) {
Supplier<S> supplier = () -> {
return new TaskImpl<>(() -> {
var s = state();
if (s.isYielded()) return yielded(s);
try {
return Bukkit.getScheduler().callSyncMethod(ScrowAPI.plugin, () -> mapper.apply(compute.get())).get();
return TaskState.completed(Bukkit.getScheduler()
.callSyncMethod(ScrowAPI.plugin, () -> mapper.apply(s.getValue()))
.get());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
};
return new TaskImpl<>(supplier);
});
}
/**
* Maps the last result async
*/
public <S> Task<S> mapAsync(Function<T, S> mapper) {
return new TaskImpl<>(() -> mapper.apply(compute.get()));
return new TaskImpl<>(() -> {
var s = state();
if (s.isYielded()) return yielded(s);
return TaskState.completed(mapper.apply(state().getValue()));
});
}
@Override
public Task<T> yieldIf(Predicate<T> precondition) {
var s = state();
if (!s.isYielded() && precondition.test(s.getValue())) {
return new TaskImpl<>(() -> TaskState.yielded(s.getValue()));
}
return this;
}
@SuppressWarnings("unchecked")
private <S> TaskState<S> yielded(TaskState<T> state) {
try {
return (TaskState<S>) state;
} catch (ClassCastException ex) {
log.error("cannot yield when type of next task differs", ex);
throw ex;
}
}
private TaskState<T> state() {
if (cachedResult == null)
cachedResult = compute.get();
return cachedResult;
}
}

View file

@ -0,0 +1,20 @@
package de.kentoj.scrow.bukkit.scheduler;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Nullable;
@Getter
@RequiredArgsConstructor
public class TaskState<T> {
private final @Nullable T value;
private final boolean yielded;
public static <T> TaskState<T> yielded(@Nullable T value) {
return new TaskState<>(value, true);
}
public static <T> TaskState<T> completed(@Nullable T value) {
return new TaskState<>(value, false);
}
}

View file

@ -5,13 +5,15 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bukkit.Bukkit;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
@NoArgsConstructor(access = AccessLevel.NONE)
public class Tasks {
public static <T> Task<T> supplyAsync(Supplier<T> supplier) {
return new TaskImpl<>(supplier);
return new TaskImpl<>(() -> TaskState.completed(supplier.get()));
}
public static Task<?> runAsync(Runnable task) {
@ -24,7 +26,9 @@ public class Tasks {
public static <T> Task<T> supplySync(Supplier<T> supplier) {
return new TaskImpl<>(() -> {
try {
return Bukkit.getScheduler().callSyncMethod(ScrowAPI.plugin, supplier::get).get();
return TaskState.completed(Bukkit.getScheduler()
.callSyncMethod(ScrowAPI.plugin, supplier::get)
.get());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
@ -40,4 +44,11 @@ public class Tasks {
return null;
});
}
public static void awaitAll(Task<?>... tasks) {
var cfs = Arrays.stream(tasks)
.map(Task::toFuture)
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(cfs).join();
}
}