diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..1f19ed9
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,26 @@
+name: Build and publish
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ publish:
+ name: Publish build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: corretto
+ java-version: 23
+
+ - name: Make Gradle executable
+ run: chmod +x ./gradlew
+
+ - name: Build and publish project
+ run: ./gradlew publish -PtommyjsUsername=${{ secrets.NEXUS_USERNAME }} -PtommyjsPassword=${{ secrets.NEXUS_PASSWORD }}
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..1eb3b4d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,23 +1,38 @@
plugins {
id 'java-library'
id 'com.github.johnrengelman.shadow' version '8.1.1'
- id 'io.github.gradle-nexus.publish-plugin' version '2.0.0'
-}
-
-nexusPublishing {
- repositories {
- tommyjs {
- nexusUrl = uri("https://repo.tommyjs.dev/repository/maven-releases")
- }
- }
+ id 'maven-publish'
}
subprojects {
group = 'dev.tommyjs'
- version = '2.4.1'
+ version = '2.5.0'
apply plugin: 'java-library'
apply plugin: 'com.github.johnrengelman.shadow'
+ apply plugin : 'maven-publish'
+
+ publishing {
+ publications {
+ mavenJava(MavenPublication) {
+ from(components["java"])
+ pom {
+ name = project.name
+ }
+ }
+ }
+
+ repositories {
+ maven {
+ name = 'tommyjs'
+ url = uri("https://repo.tommyjs.dev/repository/maven-releases/")
+ credentials {
+ username = findProperty("tommyjsUsername") as String
+ password = findProperty("tommyjsPassword") as String
+ }
+ }
+ }
+ }
tasks {
build {
@@ -48,8 +63,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..0e0da40
--- /dev/null
+++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/ExecutorImpl.java
@@ -0,0 +1,32 @@
+package dev.tommyjs.futur.executor;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+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 @Nullable PromiseScheduler> scheduler() {
+ return null;
+ }
+
+}
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..975bace 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
@@ -1,32 +1,32 @@
package dev.tommyjs.futur.executor;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+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 @Nullable PromiseScheduler> scheduler() {
+ return null;
+ }
+
}
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..86bee7b 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
@@ -1,10 +1,11 @@
package dev.tommyjs.futur.executor;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+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 +43,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,26 +67,17 @@ 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.
*
* @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.
+ * @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);
+
+ @Nullable 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..d9fdafc
--- /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..bc6c367
--- /dev/null
+++ b/futur-api/src/main/java/dev/tommyjs/futur/executor/PromiseSchedulerDefault.java
@@ -0,0 +1,28 @@
+package dev.tommyjs.futur.executor;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.*;
+
+class PromiseSchedulerDefault implements PromiseScheduler> {
+
+ static final PromiseSchedulerDefault INSTANCE = new PromiseSchedulerDefault();
+
+ private final ScheduledExecutorService executor;
+
+ PromiseSchedulerDefault() {
+ ThreadFactory factory = Thread.ofPlatform().name("promise-scheduler").daemon(true).factory();
+ this.executor = Executors.newSingleThreadScheduledExecutor(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..fcbc009
--- /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/joiner/CompletionJoiner.java b/futur-api/src/main/java/dev/tommyjs/futur/joiner/CompletionJoiner.java
index 82f9539..2faeaf7 100644
--- a/futur-api/src/main/java/dev/tommyjs/futur/joiner/CompletionJoiner.java
+++ b/futur-api/src/main/java/dev/tommyjs/futur/joiner/CompletionJoiner.java
@@ -13,11 +13,7 @@ public class CompletionJoiner extends PromiseJoiner, Void, Void, List
private final ConcurrentResultArray> results;
- public CompletionJoiner(
- @NotNull PromiseFactory factory,
- @NotNull Iterator> promises,
- int expectedSize
- ) {
+ public CompletionJoiner(@NotNull PromiseFactory factory, @NotNull Iterator> promises, int expectedSize) {
super(factory);
results = new ConcurrentResultArray<>(expectedSize);
join(promises);
diff --git a/futur-api/src/main/java/dev/tommyjs/futur/joiner/MappedResultJoiner.java b/futur-api/src/main/java/dev/tommyjs/futur/joiner/MappedResultJoiner.java
index ee64b0e..a2a5ad9 100644
--- a/futur-api/src/main/java/dev/tommyjs/futur/joiner/MappedResultJoiner.java
+++ b/futur-api/src/main/java/dev/tommyjs/futur/joiner/MappedResultJoiner.java
@@ -12,11 +12,8 @@ public class MappedResultJoiner extends PromiseJoiner> results;
- public MappedResultJoiner(
- @NotNull PromiseFactory factory,
- @NotNull Iterator>> promises,
- int expectedSize
- ) {
+ public MappedResultJoiner(@NotNull PromiseFactory factory, @NotNull Iterator>> promises,
+ int expectedSize) {
super(factory);
this.results = new ConcurrentResultArray<>(expectedSize);
join(promises);
diff --git a/futur-api/src/main/java/dev/tommyjs/futur/joiner/ResultJoiner.java b/futur-api/src/main/java/dev/tommyjs/futur/joiner/ResultJoiner.java
index b4e1a2d..3f53887 100644
--- a/futur-api/src/main/java/dev/tommyjs/futur/joiner/ResultJoiner.java
+++ b/futur-api/src/main/java/dev/tommyjs/futur/joiner/ResultJoiner.java
@@ -13,11 +13,7 @@ public class ResultJoiner extends PromiseJoiner, Void, T, List>
private final ConcurrentResultArray results;
- public ResultJoiner(
- @NotNull PromiseFactory factory,
- @NotNull Iterator> promises,
- int expectedSize
- ) {
+ public ResultJoiner(@NotNull PromiseFactory factory, @NotNull Iterator> promises, int expectedSize) {
super(factory);
this.results = new ConcurrentResultArray<>(expectedSize);
join(promises);
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..c05f705 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 unrecoverable errors
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 unrecoverable errors
throw error;
} catch (Throwable e) {
handler.accept(e);
@@ -62,9 +64,13 @@ public abstract class AbstractPromise implements Promise {
protected V useCompletion(Supplier unresolved, Function completed, Function failed) {
PromiseCompletion completion = getCompletion();
- if (completion == null) return unresolved.get();
- else if (completion.isSuccess()) return completed.apply(completion.getResult());
- else return failed.apply(completion.getException());
+ if (completion == null) {
+ return unresolved.get();
+ } else if (completion.isSuccess()) {
+ return completed.apply(completion.getResult());
+ } else {
+ return failed.apply(completion.getException());
+ }
}
protected @NotNull Runnable createCompleter(T result, @NotNull CompletablePromise promise,
@@ -104,21 +110,27 @@ 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
public @NotNull Promise fork() {
- if (isCompleted()) return this;
+ if (isCompleted()) {
+ return this;
+ }
CompletablePromise fork = getFactory().unresolved();
PromiseUtil.propagateCompletion(this, fork);
@@ -148,241 +160,243 @@ public abstract class AbstractPromise implements Promise {
@Override
public @NotNull Promise thenApply(@NotNull ExceptionalFunction task) {
- return useCompletion(
- () -> {
- CompletablePromise promise = createLinked();
- addDirectListener(
- res -> createCompleter(res, promise, task).run(),
- promise::completeExceptionally
- );
+ return useCompletion(() -> {
+ CompletablePromise promise = createLinked();
+ addDirectListener(res -> createCompleter(res, promise, task).run(), promise::completeExceptionally);
- return promise;
- },
- result -> supplySafe(
- () -> getFactory().resolve(task.apply(result)),
- getFactory()::error
- ),
- getFactory()::error
- );
+ return promise;
+ }, result -> supplySafe(() -> getFactory().resolve(task.apply(result)), getFactory()::error), getFactory()::error);
}
@Override
public @NotNull Promise thenCompose(@NotNull ExceptionalFunction> task) {
- return useCompletion(
- () -> {
- CompletablePromise promise = createLinked();
- thenApply(task).addDirectListener(
- result -> {
- if (result == null) {
- promise.complete(null);
- } else {
- PromiseUtil.propagateCompletion(result, promise);
- PromiseUtil.propagateCancel(promise, result);
- }
- },
- promise::completeExceptionally
- );
+ return useCompletion(() -> {
+ CompletablePromise promise = createLinked();
+ thenApply(task).addDirectListener(result -> {
+ if (result == null) {
+ promise.complete(null);
+ } else {
+ PromiseUtil.propagateCompletion(result, promise);
+ PromiseUtil.propagateCancel(promise, result);
+ }
+ }, promise::completeExceptionally);
+ return promise;
+ }, result -> supplySafe(() -> {
+ Promise nested = task.apply(result);
+ if (nested == null) {
+ return getFactory().resolve(null);
+ } else if (nested.isCompleted()) {
+ return nested;
+ } else {
+ CompletablePromise promise = createLinked();
+ PromiseUtil.propagateCompletion(nested, promise);
+ PromiseUtil.propagateCancel(promise, nested);
return promise;
- },
- result -> supplySafe(
- () -> {
- Promise nested = task.apply(result);
- if (nested == null) {
- return getFactory().resolve(null);
- } else if (nested.isCompleted()) {
- return nested;
- } else {
- CompletablePromise promise = createLinked();
- PromiseUtil.propagateCompletion(nested, promise);
- PromiseUtil.propagateCancel(promise, nested);
- return promise;
- }
- },
- getFactory()::error
- ),
- getFactory()::error
- );
+ }
+ }, getFactory()::error), getFactory()::error);
+ }
+
+ private @NotNull Promise thenApply(@NotNull ExceptionalFunction task,
+ @NotNull PromiseExecutor executor) {
+ CompletablePromise promise = createLinked();
+ addDirectListener(res -> runCompleter(promise, () -> {
+ Runnable completer = createCompleter(res, promise, task);
+ execute(promise, completer, executor);
+ }), promise::completeExceptionally);
+
+ return promise;
+ }
+
+ private @NotNull Promise thenApplyDelayed(@NotNull ExceptionalFunction task, long delay,
+ @NotNull TimeUnit unit, @NotNull PromiseExecutor executor) {
+ CompletablePromise promise = createLinked();
+ addDirectListener(res -> runCompleter(promise, () -> {
+ Runnable completer = createCompleter(res, promise, task);
+ PromiseScheduler> scheduler = executor.scheduler();
+ if (scheduler == null) {
+ schedule(promise, () -> runCompleter(promise, () -> execute(promise, completer, executor)), delay, unit,
+ PromiseScheduler.getDefault());
+ } else {
+ schedule(promise, completer, delay, unit, scheduler);
+ }
+ }), promise::completeExceptionally);
+
+ return promise;
+ }
+
+ private void execute(@NotNull Promise> promise, @NotNull Runnable task, @NotNull PromiseExecutor executor)
+ throws Exception {
+ F future = executor.run(task);
+ promise.addDirectListener(_ -> executor.cancel(future));
+ }
+
+ private void schedule(@NotNull Promise> promise, @NotNull Runnable task, long delay, @NotNull TimeUnit unit,
+ @NotNull PromiseScheduler scheduler) throws Exception {
+ F future = scheduler.schedule(task, delay, unit);
+ promise.addDirectListener(_ -> scheduler.cancel(future));
+ }
+
+ 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);
+ public @NotNull Promise thenRunDelayedSync(@NotNull ExceptionalRunnable task, long delay,
+ @NotNull TimeUnit unit) {
+ return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getSyncExecutor());
}
@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);
+ public @NotNull Promise thenConsumeDelayedSync(@NotNull ExceptionalConsumer task, long delay,
+ @NotNull TimeUnit unit) {
+ return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getSyncExecutor());
}
@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);
+ public @NotNull Promise thenSupplyDelayedSync(@NotNull ExceptionalSupplier task, long delay,
+ @NotNull TimeUnit unit) {
+ return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getSyncExecutor());
}
@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;
+ public @NotNull Promise thenApplyDelayedSync(@NotNull ExceptionalFunction task, long delay,
+ @NotNull TimeUnit unit) {
+ return thenApplyDelayed(task, delay, unit, getFactory().getSyncExecutor());
}
@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);
+ public @NotNull Promise thenRunDelayedAsync(@NotNull ExceptionalRunnable task, long delay,
+ @NotNull TimeUnit unit) {
+ return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getAsyncExecutor());
}
@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);
+ public @NotNull Promise thenConsumeDelayedAsync(@NotNull ExceptionalConsumer task, long delay,
+ @NotNull TimeUnit unit) {
+ return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getAsyncExecutor());
}
@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);
+ public @NotNull Promise thenSupplyDelayedAsync(@NotNull ExceptionalSupplier task, long delay,
+ @NotNull TimeUnit unit) {
+ return thenApplyDelayed(FunctionAdapter.adapt(task), delay, unit, getFactory().getAsyncExecutor());
}
@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;
+ public @NotNull Promise thenApplyDelayedAsync(@NotNull ExceptionalFunction task, long delay,
+ @NotNull TimeUnit unit) {
+ return thenApplyDelayed(task, delay, unit, getFactory().getAsyncExecutor());
}
@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());
+ }
+
+ @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());
+ }
+
+ @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());
+ }
+
+ @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());
+ }
+
+ @Override
+ public @NotNull Promise thenComposeVirtual(@NotNull ExceptionalFunction> task) {
+ return thenCompose(task, getFactory().getVirtualExecutor());
}
@Override
@@ -404,12 +418,17 @@ public abstract class AbstractPromise implements Promise {
}
@Override
- public @NotNull Promise addAsyncListener(@Nullable Consumer successListener, @Nullable Consumer errorListener) {
+ public @NotNull Promise addAsyncListener(@Nullable Consumer successListener,
+ @Nullable Consumer errorListener) {
return addAsyncListener(res -> {
if (res.isSuccess()) {
- if (successListener != null) successListener.accept(res.getResult());
+ if (successListener != null) {
+ successListener.accept(res.getResult());
+ }
} else {
- if (errorListener != null) errorListener.accept(res.getException());
+ if (errorListener != null) {
+ errorListener.accept(res.getException());
+ }
}
});
}
@@ -420,12 +439,17 @@ public abstract class AbstractPromise implements Promise {
}
@Override
- public @NotNull Promise addDirectListener(@Nullable Consumer successListener, @Nullable Consumer errorListener) {
+ public @NotNull Promise addDirectListener(@Nullable Consumer successListener,
+ @Nullable Consumer errorListener) {
return addDirectListener(res -> {
if (res.isSuccess()) {
- if (successListener != null) successListener.accept(res.getResult());
+ if (successListener != null) {
+ successListener.accept(res.getResult());
+ }
} else {
- if (errorListener != null) errorListener.accept(res.getException());
+ if (errorListener != null) {
+ errorListener.accept(res.getException());
+ }
}
});
}
@@ -445,7 +469,7 @@ public abstract class AbstractPromise implements Promise {
Exception wrapper = new DeferredExecutionException();
return onError(e -> {
if (e instanceof CancellationException && e.getMessage() == null && e.getCause() == null) {
- // Ignore cancellation exceptions without a message or cause
+ // ignore cancellation exceptions without a message or cause
return;
}
@@ -480,34 +504,11 @@ public abstract class AbstractPromise implements Promise {
@Override
public @NotNull Promise orDefault(@NotNull ExceptionalFunction function) {
- return useCompletion(
- () -> {
- CompletablePromise promise = createLinked();
- addDirectListener(promise::complete, e -> runCompleter(promise, () -> promise.complete(function.apply(e))));
- return promise;
- },
- getFactory()::resolve,
- getFactory()::error
- );
- }
-
- @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
- );
+ return useCompletion(() -> {
+ CompletablePromise promise = createLinked();
+ addDirectListener(promise::complete, e -> runCompleter(promise, () -> promise.complete(function.apply(e))));
+ return promise;
+ }, getFactory()::resolve, getFactory()::error);
}
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..c38af99 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);
}
});
@@ -41,9 +47,7 @@ public abstract class AbstractPromiseFactory implements PromiseFactory {
}
@Override
- public @NotNull Promise> combine(
- @NotNull Promise p1, @NotNull Promise p2
- ) {
+ public @NotNull Promise> combine(@NotNull Promise p1, @NotNull Promise p2) {
return all(p1, p2).thenApply(_ -> new AbstractMap.SimpleImmutableEntry<>(
Objects.requireNonNull(p1.getCompletion()).getResult(),
Objects.requireNonNull(p2.getCompletion()).getResult()
@@ -51,43 +55,45 @@ public abstract class AbstractPromiseFactory implements PromiseFactory {
}
@Override
- public @NotNull Promise