From fea0575392a4a27c50a33204247eba9e0be72c6d Mon Sep 17 00:00:00 2001 From: WhatCats Date: Wed, 28 May 2025 13:23:23 +0200 Subject: [PATCH] seperate scheduler and virtual executor promise chain methods --- README.md | 12 +- build.gradle | 6 +- .../tommyjs/futur/executor/ExecutorImpl.java | 31 ++ .../futur/executor/ExecutorServiceImpl.java | 25 +- .../futur/executor/PromiseExecutor.java | 35 +-- .../futur/executor/PromiseScheduler.java | 36 +++ .../executor/PromiseSchedulerDefault.java | 27 ++ .../futur/executor/ScheduledExecutorImpl.java | 37 +++ .../futur/executor/VirtualThreadImpl.java | 13 +- .../futur/function/FunctionAdapter.java | 25 ++ .../futur/promise/AbstractPromise.java | 275 +++++++++--------- .../futur/promise/AbstractPromiseFactory.java | 18 +- .../futur/promise/AsyncPromiseListener.java | 1 + .../tommyjs/futur/promise/BasePromise.java | 52 +++- .../futur/promise/CompletedPromise.java | 26 +- .../dev/tommyjs/futur/promise/Promise.java | 134 ++++++++- .../futur/promise/PromiseCompletion.java | 40 ++- .../tommyjs/futur/promise/PromiseFactory.java | 4 +- .../futur/promise/PromiseFactoryImpl.java | 22 +- .../java/dev/tommyjs/futur/PromiseTests.java | 4 +- gradle/wrapper/gradle-wrapper.properties | 3 +- 21 files changed, 595 insertions(+), 231 deletions(-) create mode 100644 futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorImpl.java create mode 100644 futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseScheduler.java create mode 100644 futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseSchedulerDefault.java create mode 100644 futur-api/src/main/java/dev/tommyjs/futur/executor/ScheduledExecutorImpl.java create mode 100644 futur-api/src/main/java/dev/tommyjs/futur/function/FunctionAdapter.java diff --git a/README.md b/README.md index 8e94b11..efc8187 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ repositories { } dependencies { - compile 'dev.tommyjs:futur-api:2.4.0' - compile 'dev.tommyjs:futur-lazy:2.4.0' + compile 'dev.tommyjs:futur-api:2.5.0' + compile 'dev.tommyjs:futur-lazy:2.5.0' } ``` ### Gradle DSL @@ -25,8 +25,8 @@ repositories { } dependencies { - implementation("dev.tommyjs:futur-api:2.4.0") - implementation("dev.tommyjs:futur-lazy:2.4.0") + implementation("dev.tommyjs:futur-api:2.5.0") + implementation("dev.tommyjs:futur-lazy:2.5.0") } ``` ### Maven @@ -42,12 +42,12 @@ dependencies { dev.tommyjs futur-api - 2.4.0 + 2.5.0 dev.tommyjs futur-lazy - 2.4.0 + 2.5.0 ``` \ No newline at end of file diff --git a/build.gradle b/build.gradle index ffd6e04..88740aa 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ nexusPublishing { subprojects { group = 'dev.tommyjs' - version = '2.4.1' + version = '2.5.0' apply plugin: 'java-library' apply plugin: 'com.github.johnrengelman.shadow' @@ -48,8 +48,8 @@ subprojects { } java { - sourceCompatibility = JavaVersion.VERSION_22 - targetCompatibility = JavaVersion.VERSION_22 + sourceCompatibility = JavaVersion.VERSION_23 + targetCompatibility = JavaVersion.VERSION_23 withSourcesJar() } } \ No newline at end of file diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorImpl.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorImpl.java new file mode 100644 index 0000000..dbed16d --- /dev/null +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorImpl.java @@ -0,0 +1,31 @@ +package dev.tommyjs.futur.executor; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.Executor; + +class ExecutorImpl implements PromiseExecutor { + + private final Executor executor; + + public ExecutorImpl(@NotNull Executor executor) { + this.executor = executor; + } + + @Override + public @NotNull Void run(@NotNull Runnable task) { + executor.execute(task); + return null; + } + + @Override + public boolean cancel(@NotNull Void task) { + return false; + } + + @Override + public @NotNull PromiseScheduler scheduler() { + return PromiseScheduler.getDefault(); + } + +} diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorServiceImpl.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorServiceImpl.java index acefe38..98867fb 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorServiceImpl.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorServiceImpl.java @@ -2,31 +2,30 @@ package dev.tommyjs.futur.executor; import org.jetbrains.annotations.NotNull; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; class ExecutorServiceImpl implements PromiseExecutor> { - private final ScheduledExecutorService service; + private final ExecutorService executor; - public ExecutorServiceImpl(@NotNull ScheduledExecutorService service) { - this.service = service; + public ExecutorServiceImpl(@NotNull ExecutorService executor) { + this.executor = executor; } @Override - public Future run(@NotNull Runnable task) { - return service.submit(task); + public @NotNull Future run(@NotNull Runnable task) { + return executor.submit(task); } @Override - public Future run(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { - return service.schedule(task, delay, unit); - } - - @Override - public boolean cancel(Future task) { + public boolean cancel(@NotNull Future task) { return task.cancel(true); } + @Override + public @NotNull PromiseScheduler scheduler() { + return PromiseScheduler.getDefault(); + } + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseExecutor.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseExecutor.java index 1143ec2..8389e19 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseExecutor.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseExecutor.java @@ -2,9 +2,9 @@ package dev.tommyjs.futur.executor; import org.jetbrains.annotations.NotNull; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; /** * An executor that can run tasks and schedule tasks to run in the future. @@ -42,11 +42,21 @@ public interface PromiseExecutor { /** * Creates a new {@link PromiseExecutor} that runs tasks on the given executor service. * - * @param service the executor service + * @param executor the executor service * @return the new executor */ - static PromiseExecutor of(@NotNull ScheduledExecutorService service) { - return new ExecutorServiceImpl(service); + static PromiseExecutor of(@NotNull ScheduledExecutorService executor) { + return new ScheduledExecutorImpl(executor); + } + + /** + * Creates a new {@link PromiseExecutor} that runs tasks on the given executor service. + * + * @param executor the executor service + * @return the new executor + */ + static PromiseExecutor of(@NotNull ExecutorService executor) { + return new ExecutorServiceImpl(executor); } /** @@ -56,18 +66,7 @@ public interface PromiseExecutor { * @return the task * @throws Exception if scheduling the task failed */ - T run(@NotNull Runnable task) throws Exception; - - /** - * Runs the given task after the given delay. - * - * @param task the task - * @param delay the delay - * @param unit the time unit - * @return the task - * @throws Exception if scheduling the task failed - */ - T run(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) throws Exception; + @NotNull T run(@NotNull Runnable task) throws Exception; /** * Cancels the given task if possible. This may interrupt the task mid-execution. @@ -76,6 +75,8 @@ public interface PromiseExecutor { * @return {@code true} if the task was cancelled. {@code false} if the task was already completed * or could not be cancelled. */ - boolean cancel(T task); + boolean cancel(@NotNull T task); + + @NotNull PromiseScheduler scheduler(); } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseScheduler.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseScheduler.java new file mode 100644 index 0000000..dbffd5a --- /dev/null +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseScheduler.java @@ -0,0 +1,36 @@ +package dev.tommyjs.futur.executor; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.TimeUnit; + +/** + * A scheduler for running tasks after a delay. + */ +public interface PromiseScheduler { + + static @NotNull PromiseScheduler getDefault() { + return PromiseSchedulerDefault.INSTANCE; + } + + /** + * Runs the given task after the given delay. + * + * @param task the task + * @param delay the delay + * @param unit the time unit + * @return the task + * @throws Exception if scheduling the task failed + */ + @NotNull T schedule(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) throws Exception; + + /** + * Cancels the given task if possible. This may interrupt the task mid-execution. + * + * @param task the task + * @return {@code true} if the task was cancelled. {@code false} if the task was already completed + * or could not be cancelled. + */ + boolean cancel(@NotNull T task); + +} diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseSchedulerDefault.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseSchedulerDefault.java new file mode 100644 index 0000000..950ff5b --- /dev/null +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseSchedulerDefault.java @@ -0,0 +1,27 @@ +package dev.tommyjs.futur.executor; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +class PromiseSchedulerDefault implements PromiseScheduler> { + + static final PromiseSchedulerDefault INSTANCE = new PromiseSchedulerDefault(); + + private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor( + Thread.ofPlatform().name("promise-scheduler").daemon(true).factory()); + + @Override + public @NotNull ScheduledFuture schedule(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { + return executor.schedule(task, delay, unit); + } + + @Override + public boolean cancel(@NotNull ScheduledFuture task) { + return task.cancel(true); + } + +} diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/ScheduledExecutorImpl.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/ScheduledExecutorImpl.java new file mode 100644 index 0000000..0f90f1e --- /dev/null +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/ScheduledExecutorImpl.java @@ -0,0 +1,37 @@ +package dev.tommyjs.futur.executor; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +class ScheduledExecutorImpl implements PromiseExecutor>, PromiseScheduler> { + + private final ScheduledExecutorService executor; + + public ScheduledExecutorImpl(@NotNull ScheduledExecutorService executor) { + this.executor = executor; + } + + @Override + public @NotNull Future run(@NotNull Runnable task) { + return executor.submit(task); + } + + @Override + public @NotNull Future schedule(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { + return executor.schedule(task, delay, unit); + } + + @Override + public boolean cancel(@NotNull Future task) { + return task.cancel(true); + } + + @Override + public @NotNull PromiseScheduler> scheduler() { + return this; + } + +} diff --git a/futur-api/src/main/java/dev/tommyjs/futur/executor/VirtualThreadImpl.java b/futur-api/src/main/java/dev/tommyjs/futur/executor/VirtualThreadImpl.java index 397efc3..044b74b 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/executor/VirtualThreadImpl.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/VirtualThreadImpl.java @@ -4,15 +4,15 @@ import org.jetbrains.annotations.NotNull; import java.util.concurrent.TimeUnit; -class VirtualThreadImpl implements PromiseExecutor { +class VirtualThreadImpl implements PromiseExecutor, PromiseScheduler { @Override - public Thread run(@NotNull Runnable task) { + public @NotNull Thread run(@NotNull Runnable task) { return Thread.ofVirtual().start(task); } @Override - public Thread run(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { + public @NotNull Thread schedule(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) { return Thread.ofVirtual().start(() -> { try { Thread.sleep(unit.toMillis(delay)); @@ -24,7 +24,7 @@ class VirtualThreadImpl implements PromiseExecutor { } @Override - public boolean cancel(Thread task) { + public boolean cancel(@NotNull Thread task) { if (task.isAlive()) { task.interrupt(); return true; @@ -33,4 +33,9 @@ class VirtualThreadImpl implements PromiseExecutor { } } + @Override + public @NotNull PromiseScheduler scheduler() { + return this; + } + } \ No newline at end of file diff --git a/futur-api/src/main/java/dev/tommyjs/futur/function/FunctionAdapter.java b/futur-api/src/main/java/dev/tommyjs/futur/function/FunctionAdapter.java new file mode 100644 index 0000000..33905e5 --- /dev/null +++ b/futur-api/src/main/java/dev/tommyjs/futur/function/FunctionAdapter.java @@ -0,0 +1,25 @@ +package dev.tommyjs.futur.function; + +import org.jetbrains.annotations.NotNull; + +public final class FunctionAdapter { + + public static @NotNull ExceptionalFunction adapt(@NotNull ExceptionalConsumer consumer) { + return (value) -> { + consumer.accept(value); + return null; + }; + } + + public static @NotNull ExceptionalFunction adapt(@NotNull ExceptionalRunnable runnable) { + return (_) -> { + runnable.run(); + return null; + }; + } + + public static @NotNull ExceptionalFunction adapt(@NotNull ExceptionalSupplier supplier) { + return (_) -> supplier.get(); + } + +} diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromise.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromise.java index 1cdfb28..2ff169e 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromise.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromise.java @@ -1,23 +1,25 @@ package dev.tommyjs.futur.promise; -import dev.tommyjs.futur.function.ExceptionalConsumer; -import dev.tommyjs.futur.function.ExceptionalFunction; -import dev.tommyjs.futur.function.ExceptionalRunnable; -import dev.tommyjs.futur.function.ExceptionalSupplier; +import dev.tommyjs.futur.executor.PromiseExecutor; +import dev.tommyjs.futur.executor.PromiseScheduler; +import dev.tommyjs.futur.function.*; import dev.tommyjs.futur.util.PromiseUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; -import java.util.concurrent.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -public abstract class AbstractPromise implements Promise { +public abstract class AbstractPromise implements Promise { - public abstract @NotNull AbstractPromiseFactory getFactory(); + public abstract @NotNull AbstractPromiseFactory getFactory(); protected abstract @NotNull Promise addAnyListener(@NotNull PromiseListener listener); @@ -37,7 +39,7 @@ public abstract class AbstractPromise implements Promise { try { return supplier.get(); } catch (Error error) { - // Rethrow error so the Thread can shut down + // Rethrow error so the Thread can shut down or whatever throw error; } catch (Throwable e) { return handler.apply(e); @@ -49,7 +51,7 @@ public abstract class AbstractPromise implements Promise { runnable.run(); } catch (Error error) { handler.accept(error); - // Rethrow error so the Thread can shut down + // Rethrow error so the Thread can shut down or whatever throw error; } catch (Throwable e) { handler.accept(e); @@ -104,16 +106,20 @@ public abstract class AbstractPromise implements Promise { protected T joinCompletionChecked() throws ExecutionException { PromiseCompletion completion = getCompletion(); - assert completion != null; - if (completion.isSuccess()) return completion.getResult(); - throw new ExecutionException(completion.getException()); + if (completion == null) { + throw new IllegalStateException("Promise is not completed yet."); + } + + return completion.getChecked(); } protected T joinCompletionUnchecked() { PromiseCompletion completion = getCompletion(); - assert completion != null; - if (completion.isSuccess()) return completion.getResult(); - throw new CompletionException(completion.getException()); + if (completion == null) { + throw new IllegalStateException("Promise is not completed yet."); + } + + return completion.get(); } @Override @@ -205,184 +211,190 @@ public abstract class AbstractPromise implements Promise { ); } + private @NotNull Promise thenApply(@NotNull ExceptionalFunction task, @NotNull PromiseExecutor executor) { + CompletablePromise promise = createLinked(); + addDirectListener( + res -> runCompleter(promise, () -> { + Runnable runnable = createCompleter(res, promise, task); + F future = executor.run(runnable); + promise.addDirectListener(_ -> executor.cancel(future)); + }), + promise::completeExceptionally + ); + + return promise; + } + + private @NotNull Promise thenApplyDelayed( + @NotNull ExceptionalFunction task, long delay, + @NotNull TimeUnit unit, @NotNull PromiseScheduler scheduler + ) { + CompletablePromise promise = createLinked(); + addDirectListener( + res -> runCompleter(promise, () -> { + Runnable runnable = createCompleter(res, promise, task); + F future = scheduler.schedule(runnable, delay, unit); + promise.addDirectListener(_ -> scheduler.cancel(future)); + }), + promise::completeExceptionally + ); + + return promise; + } + + private @NotNull Promise thenCompose( + @NotNull ExceptionalFunction> task, + @NotNull PromiseExecutor executor + ) { + CompletablePromise promise = createLinked(); + thenApply(task, executor).addDirectListener( + nestedPromise -> { + if (nestedPromise == null) { + promise.complete(null); + } else { + PromiseUtil.propagateCompletion(nestedPromise, promise); + PromiseUtil.propagateCancel(promise, nestedPromise); + } + }, + promise::completeExceptionally + ); + + return promise; + } + @Override public @NotNull Promise thenRunSync(@NotNull ExceptionalRunnable task) { - return thenApplySync(_ -> { - task.run(); - return null; - }); + return thenApply(FunctionAdapter.adapt(task), getFactory().getSyncExecutor()); } @Override public @NotNull Promise thenRunDelayedSync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit) { - return thenApplyDelayedSync(_ -> { - task.run(); - return null; - }, delay, unit); + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getSyncExecutor().scheduler()); } @Override public @NotNull Promise thenConsumeSync(@NotNull ExceptionalConsumer task) { - return thenApplySync(result -> { - task.accept(result); - return null; - }); + return thenApply(FunctionAdapter.adapt(task), getFactory().getSyncExecutor()); } @Override public @NotNull Promise thenConsumeDelayedSync(@NotNull ExceptionalConsumer task, long delay, @NotNull TimeUnit unit) { - return thenApplyDelayedSync(result -> { - task.accept(result); - return null; - }, delay, unit); + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getSyncExecutor().scheduler()); } @Override public @NotNull Promise thenSupplySync(@NotNull ExceptionalSupplier task) { - return thenApplySync(_ -> task.get()); + return thenApply(FunctionAdapter.adapt(task), getFactory().getSyncExecutor()); } @Override public @NotNull Promise thenSupplyDelayedSync(@NotNull ExceptionalSupplier task, long delay, @NotNull TimeUnit unit) { - return thenApplyDelayedSync(_ -> task.get(), delay, unit); + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getSyncExecutor().scheduler()); } @Override public @NotNull Promise thenApplySync(@NotNull ExceptionalFunction task) { - CompletablePromise promise = createLinked(); - addDirectListener( - res -> runCompleter(promise, () -> { - Runnable runnable = createCompleter(res, promise, task); - FS future = getFactory().getSyncExecutor().run(runnable); - promise.addDirectListener(_ -> getFactory().getSyncExecutor().cancel(future)); - }), - promise::completeExceptionally - ); - - return promise; + return thenApply(task, getFactory().getSyncExecutor()); } @Override public @NotNull Promise thenApplyDelayedSync(@NotNull ExceptionalFunction task, long delay, @NotNull TimeUnit unit) { - CompletablePromise promise = createLinked(); - addDirectListener( - res -> runCompleter(promise, () -> { - Runnable runnable = createCompleter(res, promise, task); - FS future = getFactory().getSyncExecutor().run(runnable, delay, unit); - promise.addDirectListener(_ -> getFactory().getSyncExecutor().cancel(future)); - }), - promise::completeExceptionally - ); - - return promise; + return thenApplyDelayed(task, delay, unit, getFactory().getSyncExecutor().scheduler()); } @Override public @NotNull Promise thenComposeSync(@NotNull ExceptionalFunction> task) { - CompletablePromise promise = createLinked(); - thenApplySync(task).addDirectListener( - nestedPromise -> { - if (nestedPromise == null) { - promise.complete(null); - } else { - PromiseUtil.propagateCompletion(nestedPromise, promise); - PromiseUtil.propagateCancel(promise, nestedPromise); - } - }, - promise::completeExceptionally - ); - - return promise; + return thenCompose(task, getFactory().getSyncExecutor()); } @Override public @NotNull Promise thenRunAsync(@NotNull ExceptionalRunnable task) { - return thenApplyAsync(_ -> { - task.run(); - return null; - }); + return thenApply(FunctionAdapter.adapt(task), getFactory().getAsyncExecutor()); } @Override public @NotNull Promise thenRunDelayedAsync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit) { - return thenApplyDelayedAsync(_ -> { - task.run(); - return null; - }, delay, unit); + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getAsyncExecutor().scheduler()); } @Override public @NotNull Promise thenConsumeAsync(@NotNull ExceptionalConsumer task) { - return thenApplyAsync(result -> { - task.accept(result); - return null; - }); + return thenApply(FunctionAdapter.adapt(task), getFactory().getAsyncExecutor()); } @Override public @NotNull Promise thenConsumeDelayedAsync(@NotNull ExceptionalConsumer task, long delay, @NotNull TimeUnit unit) { - return thenApplyDelayedAsync(result -> { - task.accept(result); - return null; - }, delay, unit); + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getAsyncExecutor().scheduler()); } @Override public @NotNull Promise thenSupplyAsync(@NotNull ExceptionalSupplier task) { - return thenApplyAsync(_ -> task.get()); + return thenApply(FunctionAdapter.adapt(task), getFactory().getAsyncExecutor()); } @Override public @NotNull Promise thenSupplyDelayedAsync(@NotNull ExceptionalSupplier task, long delay, @NotNull TimeUnit unit) { - return thenApplyDelayedAsync(_ -> task.get(), delay, unit); + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getAsyncExecutor().scheduler()); } @Override public @NotNull Promise thenApplyAsync(@NotNull ExceptionalFunction task) { - CompletablePromise promise = createLinked(); - addDirectListener( - (res) -> runCompleter(promise, () -> { - Runnable runnable = createCompleter(res, promise, task); - FA future = getFactory().getAsyncExecutor().run(runnable); - promise.addDirectListener(_ -> getFactory().getAsyncExecutor().cancel(future)); - }), - promise::completeExceptionally - ); - - return promise; + return thenApply(task, getFactory().getAsyncExecutor()); } @Override public @NotNull Promise thenApplyDelayedAsync(@NotNull ExceptionalFunction task, long delay, @NotNull TimeUnit unit) { - CompletablePromise promise = createLinked(); - addDirectListener( - res -> runCompleter(promise, () -> { - Runnable runnable = createCompleter(res, promise, task); - FA future = getFactory().getAsyncExecutor().run(runnable, delay, unit); - promise.addDirectListener(_ -> getFactory().getAsyncExecutor().cancel(future)); - }), - promise::completeExceptionally - ); - - return promise; + return thenApplyDelayed(task, delay, unit, getFactory().getAsyncExecutor().scheduler()); } @Override public @NotNull Promise thenComposeAsync(@NotNull ExceptionalFunction> task) { - CompletablePromise promise = createLinked(); - thenApplyAsync(task).addDirectListener( - nestedPromise -> { - if (nestedPromise == null) { - promise.complete(null); - } else { - PromiseUtil.propagateCompletion(nestedPromise, promise); - PromiseUtil.propagateCancel(promise, nestedPromise); - } - }, - promise::completeExceptionally - ); + return thenCompose(task, getFactory().getAsyncExecutor()); + } - return promise; + @Override + public @NotNull Promise thenRunVirtual(@NotNull ExceptionalRunnable task) { + return thenApply(FunctionAdapter.adapt(task), getFactory().getVirtualExecutor()); + } + + @Override + public @NotNull Promise thenRunDelayedVirtual(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit) { + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getVirtualExecutor().scheduler()); + } + + @Override + public @NotNull Promise thenConsumeVirtual(@NotNull ExceptionalConsumer task) { + return thenApply(FunctionAdapter.adapt(task), getFactory().getVirtualExecutor()); + } + + @Override + public @NotNull Promise thenConsumeDelayedVirtual(@NotNull ExceptionalConsumer task, long delay, @NotNull TimeUnit unit) { + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getVirtualExecutor().scheduler()); + } + + @Override + public @NotNull Promise thenSupplyVirtual(@NotNull ExceptionalSupplier task) { + return thenApply(FunctionAdapter.adapt(task), getFactory().getVirtualExecutor()); + } + + @Override + public @NotNull Promise thenSupplyDelayedVirtual(@NotNull ExceptionalSupplier task, long delay, @NotNull TimeUnit unit) { + return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getVirtualExecutor().scheduler()); + } + + @Override + public @NotNull Promise thenApplyVirtual(@NotNull ExceptionalFunction task) { + return thenApply(task, getFactory().getVirtualExecutor()); + } + + @Override + public @NotNull Promise thenApplyDelayedVirtual(@NotNull ExceptionalFunction task, long delay, @NotNull TimeUnit unit) { + return thenApplyDelayed(task, delay, unit, getFactory().getVirtualExecutor().scheduler()); + } + + @Override + public @NotNull Promise thenComposeVirtual(@NotNull ExceptionalFunction> task) { + return thenCompose(task, getFactory().getVirtualExecutor()); } @Override @@ -491,25 +503,6 @@ public abstract class AbstractPromise implements Promise { ); } - @Override - public @NotNull CompletableFuture toFuture() { - return useCompletion( - () -> { - CompletableFuture future = new CompletableFuture<>(); - addDirectListener(future::complete, future::completeExceptionally); - future.whenComplete((_, e) -> { - if (e instanceof CancellationException) { - cancel(); - } - }); - - return future; - }, - CompletableFuture::completedFuture, - CompletableFuture::failedFuture - ); - } - private static class DeferredExecutionException extends ExecutionException { } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java index 7ce84d1..3d78691 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/AbstractPromiseFactory.java @@ -14,22 +14,28 @@ import java.util.*; import java.util.concurrent.CompletionStage; import java.util.concurrent.Future; -public abstract class AbstractPromiseFactory implements PromiseFactory { +public abstract class AbstractPromiseFactory implements PromiseFactory { + + private static final PromiseExecutor VIRTUAL = PromiseExecutor.virtualThreaded(); public abstract @NotNull Logger getLogger(); - public abstract @NotNull PromiseExecutor getSyncExecutor(); + public abstract @NotNull PromiseExecutor getSyncExecutor(); - public abstract @NotNull PromiseExecutor getAsyncExecutor(); + public abstract @NotNull PromiseExecutor getAsyncExecutor(); + + public @NotNull PromiseExecutor getVirtualExecutor() { + return VIRTUAL; + } @Override public @NotNull Promise wrap(@NotNull CompletionStage completion, @Nullable Future future) { CompletablePromise promise = unresolved(); completion.whenComplete((v, e) -> { - if (e != null) { - promise.completeExceptionally(e); - } else { + if (e == null) { promise.complete(v); + } else { + promise.completeExceptionally(e); } }); diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/AsyncPromiseListener.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/AsyncPromiseListener.java index 799b6be..eb3974b 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/AsyncPromiseListener.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/AsyncPromiseListener.java @@ -5,4 +5,5 @@ package dev.tommyjs.futur.promise; * executed asynchronously by the {@link PromiseFactory} that created the completed promise. */ public interface AsyncPromiseListener extends PromiseListener { + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/BasePromise.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/BasePromise.java index 45c8c48..e630fe2 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/BasePromise.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/BasePromise.java @@ -1,5 +1,6 @@ package dev.tommyjs.futur.promise; +import dev.tommyjs.futur.executor.PromiseScheduler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,7 +14,7 @@ import java.util.concurrent.*; import java.util.concurrent.locks.AbstractQueuedSynchronizer; @SuppressWarnings({"FieldMayBeFinal"}) -public abstract class BasePromise extends AbstractPromise implements CompletablePromise { +public abstract class BasePromise extends AbstractPromise implements CompletablePromise { private static final VarHandle COMPLETION_HANDLE; private static final VarHandle LISTENERS_HANDLE; @@ -48,10 +49,10 @@ public abstract class BasePromise extends AbstractPromise callListeners(cmp); } - protected Promise completeExceptionallyDelayed(Throwable e, long delay, TimeUnit unit) { + protected Promise completeExceptionallyDelayed(Throwable e, long delay, TimeUnit unit, PromiseScheduler scheduler) { runCompleter(this, () -> { - FA future = getFactory().getAsyncExecutor().run(() -> completeExceptionally(e), delay, unit); - addDirectListener(_ -> getFactory().getAsyncExecutor().cancel(future)); + F future = scheduler.schedule(() -> completeExceptionally(e), delay, unit); + addDirectListener(_ -> scheduler.cancel(future)); }); return this; @@ -125,16 +126,21 @@ public abstract class BasePromise extends AbstractPromise return joinCompletionUnchecked(); } + @Override + public T getNow() { + return joinCompletionUnchecked(); + } + @Override public @NotNull Promise timeout(long time, @NotNull TimeUnit unit) { Exception e = new CancellationException("Promise timed out after " + time + " " + unit.toString().toLowerCase()); - return completeExceptionallyDelayed(e, time, unit); + return completeExceptionallyDelayed(e, time, unit, PromiseScheduler.getDefault()); } @Override public @NotNull Promise maxWaitTime(long time, @NotNull TimeUnit unit) { Exception e = new TimeoutException("Promise stopped waiting after " + time + " " + unit.toString().toLowerCase()); - return completeExceptionallyDelayed(e, time, unit); + return completeExceptionallyDelayed(e, time, unit, PromiseScheduler.getDefault()); } @Override @@ -162,6 +168,40 @@ public abstract class BasePromise extends AbstractPromise return completion; } + @Override + public @NotNull CompletableFuture toFuture() { + return useCompletion( + () -> { + CompletableFuture future = new CompletableFuture<>(); + addDirectListener(future::complete, future::completeExceptionally); + future.whenComplete((result, error) -> { + if (error == null) { + complete(result); + } else { + completeExceptionally(error); + } + }); + + return future; + }, + CompletableFuture::completedFuture, + CompletableFuture::failedFuture + ); + } + + @Override + public @NotNull CompletionStage toCompletionStage() { + return useCompletion( + () -> { + CompletableFuture future = new CompletableFuture<>(); + addDirectListener(future::complete, future::completeExceptionally); + return future; + }, + CompletableFuture::completedStage, + CompletableFuture::failedStage + ); + } + private static final class Sync extends AbstractQueuedSynchronizer { private Sync() { diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/CompletedPromise.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/CompletedPromise.java index d13fa2c..74602ce 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/CompletedPromise.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/CompletedPromise.java @@ -2,11 +2,9 @@ package dev.tommyjs.futur.promise; import org.jetbrains.annotations.NotNull; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; -public abstract class CompletedPromise extends AbstractPromise { +public abstract class CompletedPromise extends AbstractPromise { private static final PromiseCompletion EMPTY = new PromiseCompletion<>(); @@ -59,6 +57,11 @@ public abstract class CompletedPromise extends AbstractPromise getCompletion() { return completion; @@ -69,4 +72,19 @@ public abstract class CompletedPromise extends AbstractPromise toFuture() { + if (completion.isSuccess()) { + return CompletableFuture.completedFuture(completion.result()); + } + + assert completion.exception() != null; + return CompletableFuture.failedFuture(completion.exception()); + } + + @Override + public @NotNull CompletionStage toCompletionStage() { + return toFuture(); + } + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/Promise.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/Promise.java index f4628ce..cf31667 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/Promise.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/Promise.java @@ -309,6 +309,116 @@ public interface Promise { */ @NotNull Promise thenComposeAsync(@NotNull ExceptionalFunction> task); + /** + * Chains a task to be executed after this promise completes. + * The task will be executed in a virtual thread, immediately after this promise completes. + * Cancelling the returned promise will cancel this promise, and consequently any previous promises + * in the chain. + * + * @param task the task to execute + * @return a new promise that completes after the task is executed + */ + @NotNull Promise thenRunVirtual(@NotNull ExceptionalRunnable task); + + /** + * Chains a task to be executed after this promise completes. + * The task will be executed in a virtual thread after the specified delay after this + * promise completes. Cancelling the returned promise will cancel this promise, and consequently + * any previous promises in the chain. + * + * @param task the task to execute + * @param delay the amount of time to wait before executing the task + * @param unit the time unit of the delay + * @return a new promise that completes after the task is executed + */ + @NotNull Promise thenRunDelayedVirtual(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit); + + /** + * Chains a task to be executed after this promise completes. The task will be executed + * in a virtual thread immediately after this promise completes, and will be passed + * the result of this promise. Cancelling the returned promise will cancel this + * promise, and consequently any previous promises in the chain. + * + * @param task the task to execute + * @return a new promise that completes after the task is executed + */ + @NotNull Promise thenConsumeVirtual(@NotNull ExceptionalConsumer task); + + /** + * Chains a task to be executed after this promise completes. The task will be executed + * in a virtual thread after the specified delay after this promise completes, + * and will be passed the result of this promise. Cancelling the returned promise + * will cancel this promise, and consequently any previous promises in the chain. + * + * @param task the task to execute + * @param delay the amount of time to wait before executing the task + * @param unit the time unit of the delay + * @return a new promise that completes after the task is executed + */ + @NotNull Promise thenConsumeDelayedVirtual(@NotNull ExceptionalConsumer task, long delay, @NotNull TimeUnit unit); + + /** + * Chains a task to be executed after this promise completes. The task will be executed + * in a virtual thread immediately after this promise completes, and will supply a value + * to the next promise in the chain. Cancelling the returned promise will + * cancel this promise, and consequently any previous promises in the chain. + * + * @param task the task to execute + * @return a new promise that completes, after the task is executed, with the task result + */ + @NotNull Promise thenSupplyVirtual(@NotNull ExceptionalSupplier task); + + /** + * Chains a task to be executed after this promise completes. The task will be executed + * in a virtual thread after the specified delay after this promise completes, + * and will supply a value to the next promise in the chain. Cancelling the returned promise + * will cancel this promise, and consequently any previous promises in the chain. + * + * @param task the task to execute + * @param delay the amount of time to wait before executing the task + * @param unit the time unit of the delay + * @return a new promise that completes, after the task is executed, with the task result + */ + @NotNull Promise thenSupplyDelayedVirtual(@NotNull ExceptionalSupplier task, long delay, @NotNull TimeUnit unit); + + /** + * Chains a task to be executed after this promise completes. The task will be executed + * in a virtual thread immediately after this promise completes, and will apply the specified + * function to the result of this promise in order to supply a value to the next promise + * in the chain. Cancelling the returned promise will cancel this promise, and consequently + * any previous promises in the chain. + * + * @param task the task to execute + * @return a new promise that completes, after the task is executed, with the task result + */ + @NotNull Promise thenApplyVirtual(@NotNull ExceptionalFunction task); + + /** + * Chains a task to be executed after this promise completes. The task will be executed + * in a virtual thread after the specified delay after this promise completes, and will apply + * the specified function to the result of this promise in order to supply a value to the next + * promise in the chain. Cancelling the returned promise will cancel this promise, + * and consequently any previous promises in the chain. + * + * @param task the task to execute + * @param delay the amount of time to wait before executing the task + * @param unit the time unit of the delay + * @return a new promise that completes, after the task is executed, with the task result + */ + @NotNull Promise thenApplyDelayedVirtual(@NotNull ExceptionalFunction task, long delay, @NotNull TimeUnit unit); + + /** + * Chains a task to be executed after this promise completes. The task will be executed + * in a virtual thread immediately after this promise completes, and will compose the next + * promise in the chain from the result of this promise. Cancelling the returned + * promise will cancel this promise, and consequently any previous promises in the chain. + * + * @param task the task to execute + * @return a new promise that completes, once this promise and the promise returned by the task are + * complete, with the result of the task promise + */ + @NotNull Promise thenComposeVirtual(@NotNull ExceptionalFunction> task); + /** * Adds a listener to this promise that will populate the specified reference with the result of this * promise upon successful completion. The reference will not be populated if this promise completes @@ -592,6 +702,18 @@ public interface Promise { */ @Nullable PromiseCompletion getCompletion(); + /** + * This method does not block and will return the result immediately if available. + * Get result and throws a {@link CompletionException} if the promise completed exceptionally. + * If the promise has not completed yet, it will throw an {@link IllegalStateException}. + * + * @return the result of the promise + * @throws IllegalStateException if the promise has not completed yet + * @throws CancellationException if the promise was cancelled + * @throws CompletionException if the promise completed exceptionally + */ + T getNow(); + /** * Returns whether this promise has completed. * @@ -601,10 +723,18 @@ public interface Promise { /** * Converts this promise to a {@link CompletableFuture}. The returned future will complete with the - * result of this promise when it completes. + * result of this promise and the promise will complete with the result of the future. * - * @return a future that will complete with the result of this promise + * @return a future linked to this promise */ @NotNull CompletableFuture toFuture(); + /** + * Converts this promise to a {@link CompletionStage}. + * The returned stage will complete with the result of this promise. + * + * @return a completion stage linked to this promise result + */ + @NotNull CompletionStage toCompletionStage(); + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseCompletion.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseCompletion.java index 7ca6e9f..9628a7e 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseCompletion.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseCompletion.java @@ -4,14 +4,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; /** * Represents the result of a {@link Promise}, containing either an optional result or an exception. */ -public class PromiseCompletion { - - private @Nullable T result; - private @Nullable Throwable exception; +public record PromiseCompletion(@Nullable T result, @Nullable Throwable exception) { /** * Creates a new successful completion. @@ -19,7 +18,7 @@ public class PromiseCompletion { * @param result the result */ public PromiseCompletion(@Nullable T result) { - this.result = result; + this(result, null); } /** @@ -28,14 +27,14 @@ public class PromiseCompletion { * @param exception the exception */ public PromiseCompletion(@NotNull Throwable exception) { - this.exception = exception; + this(null, exception); } /** * Creates a new successful completion with a result of {@code null}. */ public PromiseCompletion() { - this((T) null); + this(null, null); } /** @@ -65,11 +64,6 @@ public class PromiseCompletion { return exception instanceof CancellationException; } - @Deprecated - public boolean wasCanceled() { - return wasCancelled(); - } - /** * Gets the result of the completion. * @@ -88,4 +82,26 @@ public class PromiseCompletion { return exception; } + /** + * Gets the result or throws a {@link CompletionException} if the completion was exceptional. + * + * @return the result of the completion + * @throws CompletionException if the completion was exceptional + */ + public T get() { + if (isSuccess()) return getResult(); + throw new CompletionException(getException()); + } + + /** + * Gets the result or throws an {@link ExecutionException} if the completion was exceptional. + * + * @return the result of the completion + * @throws ExecutionException if the completion was exceptional + */ + public T getChecked() throws ExecutionException { + if (isSuccess()) return getResult(); + throw new ExecutionException(getException()); + } + } diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java index 94ba74b..ff569d3 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactory.java @@ -31,7 +31,7 @@ public interface PromiseFactory { */ static @NotNull PromiseFactory of(@NotNull Logger logger, @NotNull PromiseExecutor syncExecutor, @NotNull PromiseExecutor asyncExecutor) { - return new PromiseFactoryImpl<>(logger, syncExecutor, asyncExecutor); + return new PromiseFactoryImpl(logger, syncExecutor, asyncExecutor); } /** @@ -42,7 +42,7 @@ public interface PromiseFactory { * @return the new promise factory */ static @NotNull PromiseFactory of(@NotNull Logger logger, @NotNull PromiseExecutor executor) { - return new PromiseFactoryImpl<>(logger, executor, executor); + return new PromiseFactoryImpl(logger, executor, executor); } /** diff --git a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactoryImpl.java b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactoryImpl.java index 7604aeb..6450ba1 100644 --- a/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactoryImpl.java +++ b/futur-api/src/main/java/dev/tommyjs/futur/promise/PromiseFactoryImpl.java @@ -5,16 +5,16 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; -public class PromiseFactoryImpl extends AbstractPromiseFactory { +public class PromiseFactoryImpl extends AbstractPromiseFactory { private final @NotNull Logger logger; - private final @NotNull PromiseExecutor syncExecutor; - private final @NotNull PromiseExecutor asyncExecutor; + private final @NotNull PromiseExecutor syncExecutor; + private final @NotNull PromiseExecutor asyncExecutor; public PromiseFactoryImpl( @NotNull Logger logger, - @NotNull PromiseExecutor syncExecutor, - @NotNull PromiseExecutor asyncExecutor + @NotNull PromiseExecutor syncExecutor, + @NotNull PromiseExecutor asyncExecutor ) { this.logger = logger; this.syncExecutor = syncExecutor; @@ -47,25 +47,25 @@ public class PromiseFactoryImpl extends AbstractPromiseFactory { } @Override - public @NotNull PromiseExecutor getSyncExecutor() { + public @NotNull PromiseExecutor getSyncExecutor() { return syncExecutor; } @Override - public @NotNull PromiseExecutor getAsyncExecutor() { + public @NotNull PromiseExecutor getAsyncExecutor() { return asyncExecutor; } - private class PromiseImpl extends BasePromise { + private class PromiseImpl extends BasePromise { @Override - public @NotNull AbstractPromiseFactory getFactory() { + public @NotNull AbstractPromiseFactory getFactory() { return PromiseFactoryImpl.this; } } - private class CompletedPromiseImpl extends CompletedPromise { + private class CompletedPromiseImpl extends CompletedPromise { public CompletedPromiseImpl(@Nullable T result) { super(new PromiseCompletion<>(result)); @@ -80,7 +80,7 @@ public class PromiseFactoryImpl extends AbstractPromiseFactory { } @Override - public @NotNull AbstractPromiseFactory getFactory() { + public @NotNull AbstractPromiseFactory getFactory() { return PromiseFactoryImpl.this; } diff --git a/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java b/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java index 5f3a8ca..8cbabbd 100644 --- a/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java +++ b/futur-api/src/test/java/dev/tommyjs/futur/PromiseTests.java @@ -245,14 +245,14 @@ public final class PromiseTests { @Test public void testImmediate1() { var promise = promises.start().thenSupply(() -> 10); - assert promise.isCompleted() && promise instanceof CompletedPromise; + 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; + assert promise.isCompleted() && promise instanceof CompletedPromise; } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a79..e3f5343 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME