currently refactoring a lot
This commit is contained in:
parent
a32805a623
commit
700b5d5d10
20 changed files with 406 additions and 292 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue