release version 2.4.0

Merge pull request #10 from tommyskeff/release/2.4
This commit is contained in:
Tommy
2025-01-10 21:40:57 +00:00
committed by tommyskeff
48 changed files with 3061 additions and 1181 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.gradle .gradle
build/ build/
node_modules/
!gradle/wrapper/gradle-wrapper.jar !gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/ !**/src/main/**/build/
!**/src/test/**/build/ !**/src/test/**/build/

128
README.md
View File

@@ -2,10 +2,9 @@
Futur4J is a powerful and intuitive open-source Java library that simplifies asynchronous task scheduling, inspired by the concept of JavaScript promises. Futur4J is a powerful and intuitive open-source Java library that simplifies asynchronous task scheduling, inspired by the concept of JavaScript promises.
**This documentation is outdated. Please don't read it.**
## Dependency ## Dependency
The Futur4J project is composed of multiple modules. It is required to include the `futur-api` module, and the other modules depend on it at runtime, however the others are optional and dependent on your use case. The Futur4J project has a `futur-api` module that provides the core functionality, and a `futur-lazy` module that provides additional static versions of factory methods. It is
recommended to use the main module for customization of logging and execution.
### Gradle ### Gradle
```gradle ```gradle
repositories { repositories {
@@ -15,8 +14,8 @@ repositories {
} }
dependencies { dependencies {
compile 'dev.tommyjs:futur-api:2.1.3' compile 'dev.tommyjs:futur-api:2.4.0'
compile 'dev.tommyjs:futur-reactor:2.1.3' compile 'dev.tommyjs:futur-lazy:2.4.0'
} }
``` ```
### Gradle DSL ### Gradle DSL
@@ -26,8 +25,8 @@ repositories {
} }
dependencies { dependencies {
implementation("dev.tommyjs:futur-api:2.1.3") implementation("dev.tommyjs:futur-api:2.4.0")
implementation("dev.tommyjs:futur-reactor:2.1.3") implementation("dev.tommyjs:futur-lazy:2.4.0")
} }
``` ```
### Maven ### Maven
@@ -43,121 +42,12 @@ dependencies {
<dependency> <dependency>
<groupId>dev.tommyjs</groupId> <groupId>dev.tommyjs</groupId>
<artifactId>futur-api</artifactId> <artifactId>futur-api</artifactId>
<version>2.1.3</version> <version>2.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>dev.tommyjs</groupId> <groupId>dev.tommyjs</groupId>
<artifactId>futur-reactor</artifactId> <artifactId>futur-lazy</artifactId>
<version>2.1.3</version> <version>2.4.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
``` ```
## Getting started
Futur4J uses an underlying `Scheduler` instance to power both synchronous and asynchronous task execution.
The library was originally built for use in Minecraft servers, and therefore has native support for a "main" thread with can be seamlessly switched to in a `Promise` chain (read more below).
It is recommended, but not required, to manually create a `Scheduler`. If this is not done, a single-threaded default `Scheduler` will be used.
```java
// check if scheduler has already been loaded
if (!Schedulers.isLoaded()) {
Scheduler scheduler = ThreadPoolScheduler.create(6); // create a scheduler using an underlying thread pool (6 threads)
Schedulers.setScheduler(scheduler); // initialize the scheduling
}
```
The `futur-standalone` module has two scheduler implementations available. The `ExclusiveThreadPoolScheduler` operates one thread pool, and throws an exception when sync tasks are attempted to be executed.
The `ThreadPoolScheduler` uses a single-threaded "main" pool and a multi-threaded async pool.
For Minecraft, Bukkit, Velocity and BungeeCord support is coming soon. Feel free to make PRs for other external library support as modules.
### Using the scheduler
You can invoke tasks using the synchronous and asynchronous scheduler `Scheduler` methods. It is worth noting that exceptions in these tasks are swallowed silently, and it is therefore better to
use promise chains, or use the `Schedulers` utility class, which will wrap tasks in a try-catch and log to the standard error.
```java
scheduler.runDelayedSync(() -> {
System.out.println("I was scheduled to execute 10 seconds later!");
}, 10L, TimeUnit.SECONDS);
Schedulers.runAsync(() -> {
throw new RuntimeException("I will be caught by the exception wrapper in the Schedulers class!");
});
```
### Reacting to task completions
You may have recieved some form of "future" instance (a task that will complete at an unspecified time in the future). This may be a future4j-native `Promise` instance, or another form of future. In the case of the latter, you may obtain a `Promise`
instance through the various available wrappers (e.g. `ReactorTransformer` for use with reactor core).
You can then add listeners to the `Promise`, which will be executed asynchronously on promise "completion". A promise is deemed completed once the task has either concluded successfully or an exception has been thrown. You can access this information in a `PromiseCompletion`,
which is passed to promise listeners. Promise listeners should not throw exceptions, and will print a stack trace if they do to avoid silent failure. However, callbacks in promise chain methods are permitted to throw exceptions as they will be passed down the chain.
```java
Promise<String> promise = doSomeAsyncProcessing();
promise.addListener(ctx -> {
if (ctx.isError()) {
System.out.println("Error! Oh no!");
} else {
String result = ctx.getResult();
System.out.println(result);
}
});
```
### Promise chains
`Promise` also contains convenience wrapper methods to avoid checking the completion for exceptions every time. These methods also return a new promise which will resolve once the original promise, and then handler callback have finished execution. We can use this feature to create
"promise chains", which are a sequence of promise handlers. These handlers are permitted to throw exceptions, which will be passed down promise chains until they are handled. If exceptions are not handled properly, they will be silently swallowed. It is recommend to append `logExceptions()`
to chains unless exceptions are handled explicitly with a custom listener. This will simply log exceptions in the promise chain to the standard error.
You can also use the sync and async methods to choose which executor is chosen for task execution. The ability to seamlessly switch between executors comes in handy with applications where you must be on the "main thread" to perform non-threadsafe operations.
`Promise.start()` can be used to create a "resolved" promise. This is useful when starting a promise chain or returning an already-completed promise (common in compose callbacks, mentioned below).
```java
Promise.start().thenSupplyAsync(() -> {
// do some async processing
return "Hello World!";
}).thenApplyAsync(input -> {
// convert string to upper case
return input.toUpperCase();
}).thenConsumeSync(result -> {
// display result
System.out.println("Result: " + result);
}).logExceptions(); // log exceptions to the standard error
```
The promise chain methods follow Java convention of `Supplier`, `Consumer` and `Runnable` through the `thenSupply`, `thenConsume` and `thenRun` methods. There is also `thenApply` (which acts as a Java `Function`), and `thenCompose`, which is explained below. Futur4J has its own implementations of the Java concepts though to allow for
exceptions within handlers.
The `thenCompose` method is similar to the concept in the Java `CompletableFuture` API. You are able to return another `Promise` within a promise handler, and a `Promise<Promise<T>>` will be wrapped up into just a `Promise<T>`. This is often useful when using an external library that returns some sort of "future" inside a handler.
```java
String username = "abc123";
mySuperSecretApi.usernameToId(username) // returns Promise<UUID>
.thenComposeAsync(id -> {
return userManager.fetchFromDatabase(id); // returns Promise<User>
}).thenConsumeSync(playerData -> {
System.out.println(username + " has " + playerData.getCoins() + " coins!");
}).logExceptions();
```
### Utility methods
The `Promises` utility class provides many helpful methods for working with promises and groups of promises.
`Promises.combine(Promise<K>, Promise<V>)` combines two promises into one `Promise<Entry<K, V>>`.
`Promises.erase(Promise)` erases the type on a `Promise` instance and returns a `Promise<Void>`. This is also supported for lists of promises with `Promises.all(List<Promise>)`, which will return a `Promise<Void>` representing the future whereby all promises have completed.
If all promises are of identical type, you can use `Promises.combine(List<Promise<T>>)` which will return one `Promise<List<T>>`, similarly representing the future whereby all promises have completed.
This can also be applied to key-value scenarios in the same way, where you can provide a mapper function to be applied to all elements and combine into a single promise.
### Future wrappers
External libaries provide asynchronous ways to interact with their output in all shapes and sizes. Futur4J currently has wrappers for the following libraries/frameworks:
- Reactor Core (via futur-reactor)
- Java CompletableFuture (via `Promises.wrap` in futur-api)

View File

@@ -1,7 +1,7 @@
plugins { plugins {
id 'java' id 'java-library'
id 'com.github.johnrengelman.shadow' version '8.1.1' id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'io.github.gradle-nexus.publish-plugin' version '1.3.0' id 'io.github.gradle-nexus.publish-plugin' version '2.0.0'
} }
nexusPublishing { nexusPublishing {
@@ -14,9 +14,9 @@ nexusPublishing {
subprojects { subprojects {
group = 'dev.tommyjs' group = 'dev.tommyjs'
version = '2.3.4' version = '2.4.0'
apply plugin: 'java' apply plugin: 'java-library'
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'
tasks { tasks {
@@ -30,8 +30,9 @@ subprojects {
} }
dependencies { dependencies {
implementation 'org.jetbrains:annotations:24.1.0' compileOnly 'org.jetbrains:annotations:24.1.0'
implementation 'org.slf4j:slf4j-api:2.0.12' implementation 'org.slf4j:slf4j-api:2.0.12'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'io.projectreactor:reactor-core:3.6.4' testImplementation 'io.projectreactor:reactor-core:3.6.4'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1'
@@ -42,6 +43,13 @@ subprojects {
useJUnitPlatform() useJUnitPlatform()
testLogging { testLogging {
exceptionFormat = 'full' exceptionFormat = 'full'
showStandardStreams = true
} }
} }
java {
sourceCompatibility = JavaVersion.VERSION_22
targetCompatibility = JavaVersion.VERSION_22
withSourcesJar()
}
} }

View File

@@ -1,39 +0,0 @@
package dev.tommyjs.futur.executor;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class DualPoolExecutor implements PromiseExecutor<Future<?>> {
private final @NotNull ScheduledExecutorService syncSvc;
private final @NotNull ScheduledExecutorService asyncSvc;
public DualPoolExecutor(@NotNull ScheduledExecutorService syncSvc, @NotNull ScheduledExecutorService asyncSvc) {
this.syncSvc = syncSvc;
this.asyncSvc = asyncSvc;
}
public static @NotNull DualPoolExecutor create(int asyncPoolSize) {
return new DualPoolExecutor(Executors.newSingleThreadScheduledExecutor(), Executors.newScheduledThreadPool(asyncPoolSize));
}
@Override
public Future<?> runSync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) {
return syncSvc.schedule(task, delay, unit);
}
@Override
public Future<?> runAsync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) {
return asyncSvc.schedule(task, delay, unit);
}
@Override
public void cancel(Future<?> task) {
task.cancel(true);
}
}

View File

@@ -0,0 +1,32 @@
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 ExecutorServiceImpl implements PromiseExecutor<Future<?>> {
private final ScheduledExecutorService service;
public ExecutorServiceImpl(@NotNull ScheduledExecutorService service) {
this.service = service;
}
@Override
public Future<?> run(@NotNull Runnable task) {
return service.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) {
return task.cancel(true);
}
}

View File

@@ -2,22 +2,80 @@ package dev.tommyjs.futur.executor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/**
* An executor that can run tasks and schedule tasks to run in the future.
*/
public interface PromiseExecutor<T> { public interface PromiseExecutor<T> {
T runSync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit); /**
* Creates a new {@link PromiseExecutor} that runs tasks on virtual threads.
T runAsync(@NotNull Runnable task, long delay, @NotNull TimeUnit unit); *
* @return the new executor
default T runSync(@NotNull Runnable task) { */
return runSync(task, 0L, TimeUnit.MILLISECONDS); static PromiseExecutor<?> virtualThreaded() {
return new VirtualThreadImpl();
} }
default T runAsync(@NotNull Runnable task) { /**
return runAsync(task, 0L, TimeUnit.MILLISECONDS); * Creates a new {@link PromiseExecutor} that runs tasks on a single thread.
*
* @return the new executor
*/
static PromiseExecutor<?> singleThreaded() {
return of(Executors.newSingleThreadScheduledExecutor());
} }
void cancel(T task); /**
* Creates a new {@link PromiseExecutor} that runs tasks on multiple threads.
*
* @param threads the number of threads
* @return the new executor
*/
static PromiseExecutor<?> multiThreaded(int threads) {
return of(Executors.newScheduledThreadPool(threads));
}
/**
* Creates a new {@link PromiseExecutor} that runs tasks on the given executor service.
*
* @param service the executor service
* @return the new executor
*/
static PromiseExecutor<?> of(@NotNull ScheduledExecutorService service) {
return new ExecutorServiceImpl(service);
}
/**
* Runs the given task.
*
* @param task the task
* @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;
/**
* 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(T task);
} }

View File

@@ -1,18 +0,0 @@
package dev.tommyjs.futur.executor;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
public class SinglePoolExecutor extends DualPoolExecutor {
public SinglePoolExecutor(@NotNull ScheduledExecutorService service) {
super(service, service);
}
public static @NotNull SinglePoolExecutor create(int threadPoolSize) {
return new SinglePoolExecutor(Executors.newScheduledThreadPool(threadPoolSize));
}
}

View File

@@ -0,0 +1,36 @@
package dev.tommyjs.futur.executor;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.TimeUnit;
class VirtualThreadImpl implements PromiseExecutor<Thread> {
@Override
public Thread run(@NotNull Runnable task) {
return Thread.ofVirtual().start(task);
}
@Override
public Thread run(@NotNull Runnable task, long delay, @NotNull TimeUnit unit) {
return Thread.ofVirtual().start(() -> {
try {
Thread.sleep(unit.toMillis(delay));
} catch (InterruptedException e) {
return;
}
task.run();
});
}
@Override
public boolean cancel(Thread task) {
if (task.isAlive()) {
task.interrupt();
return true;
} else {
return false;
}
}
}

View File

@@ -1,8 +1,20 @@
package dev.tommyjs.futur.function; package dev.tommyjs.futur.function;
/**
* Represents an operation that accepts a single input argument and returns no result,
* and may throw an exception. This is a functional interface whose functional method is {@link #accept(Object)}.
*
* @param <T> the type of the input to the operation
*/
@FunctionalInterface @FunctionalInterface
public interface ExceptionalConsumer<T> { public interface ExceptionalConsumer<T> {
void accept(T value) throws Throwable; /**
* Performs this operation on the given argument, potentially throwing an exception.
*
* @param value the input argument
* @throws Exception if unable to compute a result
*/
void accept(T value) throws Exception;
} }

View File

@@ -1,8 +1,22 @@
package dev.tommyjs.futur.function; package dev.tommyjs.futur.function;
/**
* Represents a function that accepts one argument and produces a result,
* and may throw an exception. This is a functional interface whose functional method is {@link #apply(Object)}.
*
* @param <K> the type of the input to the function
* @param <V> the type of the result of the function
*/
@FunctionalInterface @FunctionalInterface
public interface ExceptionalFunction<K, V> { public interface ExceptionalFunction<K, V> {
V apply(K value) throws Throwable; /**
* Applies this function to the given argument, potentially throwing an exception.
*
* @param value the input argument
* @return the function result
* @throws Exception if unable to compute a result
*/
V apply(K value) throws Exception;
} }

View File

@@ -1,8 +1,17 @@
package dev.tommyjs.futur.function; package dev.tommyjs.futur.function;
/**
* Represents a runnable task that may throw an exception.
* This is a functional interface whose functional method is {@link #run()}.
*/
@FunctionalInterface @FunctionalInterface
public interface ExceptionalRunnable { public interface ExceptionalRunnable {
void run() throws Throwable; /**
* Performs this runnable task, potentially throwing an exception.
*
* @throws Exception if unable to complete the task
*/
void run() throws Exception;
} }

View File

@@ -1,8 +1,20 @@
package dev.tommyjs.futur.function; package dev.tommyjs.futur.function;
/**
* Represents a supplier of results that may throw an exception.
* This is a functional interface whose functional method is {@link #get()}.
*
* @param <T> the type of results supplied by this supplier
*/
@FunctionalInterface @FunctionalInterface
public interface ExceptionalSupplier<T> { public interface ExceptionalSupplier<T> {
T get() throws Throwable; /**
* Gets a result, potentially throwing an exception.
*
* @return a result
* @throws Exception if unable to supply a result
*/
T get() throws Exception;
} }

View File

@@ -1,27 +0,0 @@
package dev.tommyjs.futur.impl;
import dev.tommyjs.futur.executor.PromiseExecutor;
import dev.tommyjs.futur.promise.AbstractPromise;
import dev.tommyjs.futur.promise.AbstractPromiseFactory;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
public class SimplePromise<T, F> extends AbstractPromise<T, F> {
private final @NotNull AbstractPromiseFactory<F> factory;
public SimplePromise(@NotNull AbstractPromiseFactory<F> factory) {
this.factory = factory;
}
@Deprecated
public SimplePromise(@NotNull PromiseExecutor<F> executor, @NotNull Logger logger, @NotNull AbstractPromiseFactory<F> factory) {
this(factory);
}
@Override
public @NotNull AbstractPromiseFactory<F> getFactory() {
return factory;
}
}

View File

@@ -1,34 +0,0 @@
package dev.tommyjs.futur.impl;
import dev.tommyjs.futur.executor.PromiseExecutor;
import dev.tommyjs.futur.promise.AbstractPromiseFactory;
import dev.tommyjs.futur.promise.Promise;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
public class SimplePromiseFactory<F> extends AbstractPromiseFactory<F> {
private final PromiseExecutor<F> executor;
private final Logger logger;
public SimplePromiseFactory(PromiseExecutor<F> executor, Logger logger) {
this.executor = executor;
this.logger = logger;
}
@Override
public @NotNull <T> Promise<T> unresolved() {
return new SimplePromise<>(this);
}
@Override
public @NotNull Logger getLogger() {
return logger;
}
@Override
public @NotNull PromiseExecutor<F> getExecutor() {
return executor;
}
}

View File

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

View File

@@ -0,0 +1,51 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import dev.tommyjs.futur.util.ConcurrentResultArray;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class MappedResultJoiner<K, V> extends PromiseJoiner<Map.Entry<K, Promise<V>>, K, V, Map<K, V>> {
private final @NotNull ConcurrentResultArray<Map.Entry<K, V>> results;
public MappedResultJoiner(
@NotNull PromiseFactory factory,
@NotNull Iterator<Map.Entry<K, Promise<V>>> promises,
int expectedSize
) {
super(factory);
this.results = new ConcurrentResultArray<>(expectedSize);
join(promises);
}
@Override
protected K getChildKey(Map.Entry<K, Promise<V>> entry) {
return entry.getKey();
}
@Override
protected @NotNull Promise<V> getChildPromise(Map.Entry<K, Promise<V>> entry) {
return entry.getValue();
}
@Override
protected void onChildComplete(int index, K key, @NotNull PromiseCompletion<V> res) {
results.set(index, new AbstractMap.SimpleImmutableEntry<>(key, res.getResult()));
}
@Override
protected Map<K, V> getResult() {
List<Map.Entry<K, V>> list = results.toList();
Map<K, V> map = new HashMap<>(list.size());
for (Map.Entry<K, V> entry : list) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
}

View File

@@ -0,0 +1,69 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.CompletablePromise;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import dev.tommyjs.futur.util.PromiseUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class PromiseJoiner<T, Key, Value, Result> {
private final CompletablePromise<Result> joined;
protected PromiseJoiner(@NotNull PromiseFactory factory) {
this.joined = factory.unresolved();
}
protected abstract Key getChildKey(T value);
protected abstract @NotNull Promise<Value> getChildPromise(T value);
protected abstract void onChildComplete(int index, Key key, @NotNull PromiseCompletion<Value> completion);
protected abstract Result getResult();
protected void join(@NotNull Iterator<T> promises) {
AtomicInteger count = new AtomicInteger();
int i = 0;
do {
if (joined.isCompleted()) {
promises.forEachRemaining(v -> getChildPromise(v).cancel());
return;
}
T value = promises.next();
Promise<Value> p = getChildPromise(value);
if (!p.isCompleted()) {
PromiseUtil.cancelOnComplete(joined, p);
}
count.incrementAndGet();
Key key = getChildKey(value);
int index = i++;
p.addAsyncListener(res -> {
onChildComplete(index, key, res);
if (res.isError()) {
assert res.getException() != null;
joined.completeExceptionally(res.getException());
} else if (count.decrementAndGet() == -1) {
joined.complete(getResult());
}
});
} while (promises.hasNext());
if (count.decrementAndGet() == -1) {
joined.complete(getResult());
}
}
public @NotNull Promise<Result> joined() {
return joined;
}
}

View File

@@ -0,0 +1,46 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import dev.tommyjs.futur.util.ConcurrentResultArray;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.List;
public class ResultJoiner<T> extends PromiseJoiner<Promise<T>, Void, T, List<T>> {
private final ConcurrentResultArray<T> results;
public ResultJoiner(
@NotNull PromiseFactory factory,
@NotNull Iterator<Promise<T>> promises,
int expectedSize
) {
super(factory);
this.results = new ConcurrentResultArray<>(expectedSize);
join(promises);
}
@Override
protected Void getChildKey(Promise<T> value) {
return null;
}
@Override
protected @NotNull Promise<T> getChildPromise(Promise<T> value) {
return value;
}
@Override
protected void onChildComplete(int index, Void key, @NotNull PromiseCompletion<T> res) {
results.set(index, res.getResult());
}
@Override
protected List<T> getResult() {
return results.toList();
}
}

View File

@@ -0,0 +1,38 @@
package dev.tommyjs.futur.joiner;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
public class VoidJoiner extends PromiseJoiner<Promise<?>, Void, Void, Void> {
public VoidJoiner(@NotNull PromiseFactory factory, @NotNull Iterator<Promise<?>> promises) {
super(factory);
join(promises);
}
@Override
protected Void getChildKey(Promise<?> value) {
return null;
}
@Override
protected @NotNull Promise<Void> getChildPromise(Promise<?> value) {
//noinspection unchecked
return (Promise<Void>) value;
}
@Override
protected void onChildComplete(int index, Void key, @NotNull PromiseCompletion<Void> completion) {
}
@Override
protected Void getResult() {
return null;
}
}

View File

@@ -1,111 +1,133 @@
package dev.tommyjs.futur.promise; package dev.tommyjs.futur.promise;
import dev.tommyjs.futur.executor.PromiseExecutor;
import dev.tommyjs.futur.function.ExceptionalConsumer; import dev.tommyjs.futur.function.ExceptionalConsumer;
import dev.tommyjs.futur.function.ExceptionalFunction; import dev.tommyjs.futur.function.ExceptionalFunction;
import dev.tommyjs.futur.function.ExceptionalRunnable; import dev.tommyjs.futur.function.ExceptionalRunnable;
import dev.tommyjs.futur.function.ExceptionalSupplier; import dev.tommyjs.futur.function.ExceptionalSupplier;
import dev.tommyjs.futur.util.PromiseUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Objects;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public abstract class AbstractPromise<T, F> implements Promise<T> { public abstract class AbstractPromise<T, FS, FA> implements Promise<T> {
private Collection<PromiseListener<T>> listeners; public abstract @NotNull AbstractPromiseFactory<FS, FA> getFactory();
private final AtomicReference<PromiseCompletion<T>> completion;
private final CountDownLatch latch;
private final Lock lock;
public AbstractPromise() { protected abstract @NotNull Promise<T> addAnyListener(@NotNull PromiseListener<T> listener);
this.completion = new AtomicReference<>();
this.latch = new CountDownLatch(1);
this.lock = new ReentrantLock();
}
protected static <V> void propagateResult(Promise<V> from, Promise<V> to) {
from.addDirectListener(to::complete, to::completeExceptionally);
}
protected static void propagateCancel(Promise<?> from, Promise<?> to) {
from.onCancel(to::completeExceptionally);
}
private <V> @NotNull Runnable createRunnable(T result, @NotNull Promise<V> promise, @NotNull ExceptionalFunction<T, V> task) {
return () -> {
if (promise.isCompleted()) return;
try {
V nextResult = task.apply(result);
promise.complete(nextResult);
} catch (Throwable e) {
promise.completeExceptionally(e);
}
};
}
public abstract @NotNull AbstractPromiseFactory<F> getFactory();
protected @NotNull PromiseExecutor<F> getExecutor() {
return getFactory().getExecutor();
}
protected @NotNull Logger getLogger() { protected @NotNull Logger getLogger() {
return getFactory().getLogger(); return getFactory().getLogger();
} }
@Override protected void callListener(@NotNull PromiseListener<T> listener, @NotNull PromiseCompletion<T> cmp) {
public T awaitInterruptibly() throws InterruptedException { if (listener instanceof AsyncPromiseListener) {
this.latch.await(); callListenerAsync(listener, cmp);
return joinCompletion(Objects.requireNonNull(getCompletion())); } else {
callListenerNow(listener, cmp);
}
} }
@Override protected <V> V supplySafe(@NotNull ExceptionalSupplier<V> supplier, @NotNull Function<Throwable, V> handler) {
public T awaitInterruptibly(long timeoutMillis) throws TimeoutException, InterruptedException {
boolean success = this.latch.await(timeoutMillis, TimeUnit.MILLISECONDS);
if (!success) {
throw new TimeoutException("Promise stopped waiting after " + timeoutMillis + "ms");
}
return joinCompletion(Objects.requireNonNull(getCompletion()));
}
@Override
public T await() {
try { try {
return awaitInterruptibly(); return supplier.get();
} catch (InterruptedException e) { } catch (Error error) {
throw new RuntimeException(e); // Rethrow error so the Thread can shut down
throw error;
} catch (Throwable e) {
return handler.apply(e);
} }
} }
protected void runSafe(@NotNull ExceptionalRunnable runnable, @NotNull Consumer<Throwable> handler) {
try {
runnable.run();
} catch (Error error) {
handler.accept(error);
// Rethrow error so the Thread can shut down
throw error;
} catch (Throwable e) {
handler.accept(e);
}
}
protected void runCompleter(@NotNull CompletablePromise<?> promise, @NotNull ExceptionalRunnable completer) {
runSafe(completer, promise::completeExceptionally);
}
protected <V> V useCompletion(Supplier<V> unresolved, Function<T, V> completed, Function<Throwable, V> failed) {
PromiseCompletion<T> completion = getCompletion();
if (completion == null) return unresolved.get();
else if (completion.isSuccess()) return completed.apply(completion.getResult());
else return failed.apply(completion.getException());
}
protected <V> @NotNull Runnable createCompleter(T result, @NotNull CompletablePromise<V> promise,
@NotNull ExceptionalFunction<T, V> completer) {
return () -> {
if (!promise.isCompleted()) {
runCompleter(promise, () -> promise.complete(completer.apply(result)));
}
};
}
protected <V> @NotNull CompletablePromise<V> createLinked() {
CompletablePromise<V> promise = getFactory().unresolved();
PromiseUtil.propagateCancel(promise, this);
return promise;
}
protected void callListenerAsync(PromiseListener<T> listener, PromiseCompletion<T> res) {
try {
getFactory().getAsyncExecutor().run(() -> callListenerNow(listener, res));
} catch (RejectedExecutionException ignored) {
} catch (Exception e) {
getLogger().warn("Exception caught while running promise listener", e);
}
}
protected void callListenerNow(PromiseListener<T> listener, PromiseCompletion<T> res) {
runSafe(() -> listener.handle(res), e -> getLogger().error("Exception caught in promise listener", e));
}
protected void callListenerAsyncLastResort(PromiseListener<T> listener, PromiseCompletion<T> completion) {
try {
getFactory().getAsyncExecutor().run(() -> callListenerNow(listener, completion));
} catch (Throwable ignored) {
}
}
protected T joinCompletionChecked() throws ExecutionException {
PromiseCompletion<T> completion = getCompletion();
assert completion != null;
if (completion.isSuccess()) return completion.getResult();
throw new ExecutionException(completion.getException());
}
protected T joinCompletionUnchecked() {
PromiseCompletion<T> completion = getCompletion();
assert completion != null;
if (completion.isSuccess()) return completion.getResult();
throw new CompletionException(completion.getException());
}
@Override @Override
public T await(long timeoutMillis) throws TimeoutException { public @NotNull Promise<T> fork() {
try { if (isCompleted()) return this;
return awaitInterruptibly(timeoutMillis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private T joinCompletion(PromiseCompletion<T> completion) { CompletablePromise<T> fork = getFactory().unresolved();
if (completion.isError()) PromiseUtil.propagateCompletion(this, fork);
throw new RuntimeException(completion.getException()); return fork;
return completion.getResult();
} }
@Override @Override
public @NotNull Promise<Void> thenRun(@NotNull ExceptionalRunnable task) { public @NotNull Promise<Void> thenRun(@NotNull ExceptionalRunnable task) {
return thenApply(result -> { return thenApply(_ -> {
task.run(); task.run();
return null; return null;
}); });
@@ -121,43 +143,71 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
@Override @Override
public <V> @NotNull Promise<V> thenSupply(@NotNull ExceptionalSupplier<V> task) { public <V> @NotNull Promise<V> thenSupply(@NotNull ExceptionalSupplier<V> task) {
return thenApply(result -> task.get()); return thenApply(_ -> task.get());
} }
@Override @Override
public <V> @NotNull Promise<V> thenApply(@NotNull ExceptionalFunction<T, V> task) { public <V> @NotNull Promise<V> thenApply(@NotNull ExceptionalFunction<T, V> task) {
Promise<V> promise = getFactory().unresolved(); return useCompletion(
() -> {
CompletablePromise<V> promise = createLinked();
addDirectListener( addDirectListener(
res -> createRunnable(res, promise, task).run(), res -> createCompleter(res, promise, task).run(),
promise::completeExceptionally promise::completeExceptionally
); );
propagateCancel(promise, this);
return promise; return promise;
},
result -> supplySafe(
() -> getFactory().resolve(task.apply(result)),
getFactory()::error
),
getFactory()::error
);
} }
@Override @Override
public <V> @NotNull Promise<V> thenCompose(@NotNull ExceptionalFunction<T, Promise<V>> task) { public <V> @NotNull Promise<V> thenCompose(@NotNull ExceptionalFunction<T, Promise<V>> task) {
Promise<V> promise = getFactory().unresolved(); return useCompletion(
() -> {
CompletablePromise<V> promise = createLinked();
thenApply(task).addDirectListener( thenApply(task).addDirectListener(
nestedPromise -> { result -> {
if (nestedPromise == null) { if (result == null) {
promise.complete(null); promise.complete(null);
} else { } else {
propagateResult(nestedPromise, promise); PromiseUtil.propagateCompletion(result, promise);
propagateCancel(promise, nestedPromise); PromiseUtil.propagateCancel(promise, result);
} }
}, },
promise::completeExceptionally promise::completeExceptionally
); );
propagateCancel(promise, this);
return promise; return promise;
},
result -> supplySafe(
() -> {
Promise<V> nested = task.apply(result);
if (nested == null) {
return getFactory().resolve(null);
} else if (nested.isCompleted()) {
return nested;
} else {
CompletablePromise<V> promise = createLinked();
PromiseUtil.propagateCompletion(nested, promise);
PromiseUtil.propagateCancel(promise, nested);
return promise;
}
},
getFactory()::error
),
getFactory()::error
);
} }
@Override @Override
public @NotNull Promise<Void> thenRunSync(@NotNull ExceptionalRunnable task) { public @NotNull Promise<Void> thenRunSync(@NotNull ExceptionalRunnable task) {
return thenApplySync(result -> { return thenApplySync(_ -> {
task.run(); task.run();
return null; return null;
}); });
@@ -165,7 +215,7 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
@Override @Override
public @NotNull Promise<Void> thenRunDelayedSync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit) { public @NotNull Promise<Void> thenRunDelayedSync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit) {
return thenApplyDelayedSync(result -> { return thenApplyDelayedSync(_ -> {
task.run(); task.run();
return null; return null;
}, delay, unit); }, delay, unit);
@@ -189,76 +239,65 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
@Override @Override
public <V> @NotNull Promise<V> thenSupplySync(@NotNull ExceptionalSupplier<V> task) { public <V> @NotNull Promise<V> thenSupplySync(@NotNull ExceptionalSupplier<V> task) {
return thenApplySync(result -> task.get()); return thenApplySync(_ -> task.get());
} }
@Override @Override
public <V> @NotNull Promise<V> thenSupplyDelayedSync(@NotNull ExceptionalSupplier<V> task, long delay, @NotNull TimeUnit unit) { public <V> @NotNull Promise<V> thenSupplyDelayedSync(@NotNull ExceptionalSupplier<V> task, long delay, @NotNull TimeUnit unit) {
return thenApplyDelayedSync(result -> task.get(), delay, unit); return thenApplyDelayedSync(_ -> task.get(), delay, unit);
} }
@Override @Override
public <V> @NotNull Promise<V> thenApplySync(@NotNull ExceptionalFunction<T, V> task) { public <V> @NotNull Promise<V> thenApplySync(@NotNull ExceptionalFunction<T, V> task) {
Promise<V> promise = getFactory().unresolved(); CompletablePromise<V> promise = createLinked();
addDirectListener( addDirectListener(
res -> { res -> runCompleter(promise, () -> {
try { Runnable runnable = createCompleter(res, promise, task);
Runnable runnable = createRunnable(res, promise, task); FS future = getFactory().getSyncExecutor().run(runnable);
F future = getExecutor().runSync(runnable); promise.addDirectListener(_ -> getFactory().getSyncExecutor().cancel(future));
promise.onCancel((e) -> getExecutor().cancel(future)); }),
} catch (RejectedExecutionException e) {
promise.completeExceptionally(e);
}
},
promise::completeExceptionally promise::completeExceptionally
); );
propagateCancel(promise, this);
return promise; return promise;
} }
@Override @Override
public <V> @NotNull Promise<V> thenApplyDelayedSync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit) { public <V> @NotNull Promise<V> thenApplyDelayedSync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit) {
Promise<V> promise = getFactory().unresolved(); CompletablePromise<V> promise = createLinked();
addDirectListener( addDirectListener(
res -> { res -> runCompleter(promise, () -> {
try { Runnable runnable = createCompleter(res, promise, task);
Runnable runnable = createRunnable(res, promise, task); FS future = getFactory().getSyncExecutor().run(runnable, delay, unit);
F future = getExecutor().runSync(runnable, delay, unit); promise.addDirectListener(_ -> getFactory().getSyncExecutor().cancel(future));
promise.onCancel((e) -> getExecutor().cancel(future)); }),
} catch (RejectedExecutionException e) {
promise.completeExceptionally(e);
}
},
promise::completeExceptionally promise::completeExceptionally
); );
propagateCancel(promise, this);
return promise; return promise;
} }
@Override @Override
public <V> @NotNull Promise<V> thenComposeSync(@NotNull ExceptionalFunction<T, Promise<V>> task) { public <V> @NotNull Promise<V> thenComposeSync(@NotNull ExceptionalFunction<T, Promise<V>> task) {
Promise<V> promise = getFactory().unresolved(); CompletablePromise<V> promise = createLinked();
thenApplySync(task).addDirectListener( thenApplySync(task).addDirectListener(
nestedPromise -> { nestedPromise -> {
if (nestedPromise == null) { if (nestedPromise == null) {
promise.complete(null); promise.complete(null);
} else { } else {
propagateResult(nestedPromise, promise); PromiseUtil.propagateCompletion(nestedPromise, promise);
propagateCancel(promise, nestedPromise); PromiseUtil.propagateCancel(promise, nestedPromise);
} }
}, },
promise::completeExceptionally promise::completeExceptionally
); );
propagateCancel(promise, this);
return promise; return promise;
} }
@Override @Override
public @NotNull Promise<Void> thenRunAsync(@NotNull ExceptionalRunnable task) { public @NotNull Promise<Void> thenRunAsync(@NotNull ExceptionalRunnable task) {
return thenApplyAsync(result -> { return thenApplyAsync(_ -> {
task.run(); task.run();
return null; return null;
}); });
@@ -266,7 +305,7 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
@Override @Override
public @NotNull Promise<Void> thenRunDelayedAsync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit) { public @NotNull Promise<Void> thenRunDelayedAsync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit) {
return thenApplyDelayedAsync(result -> { return thenApplyDelayedAsync(_ -> {
task.run(); task.run();
return null; return null;
}, delay, unit); }, delay, unit);
@@ -290,84 +329,73 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
@Override @Override
public <V> @NotNull Promise<V> thenSupplyAsync(@NotNull ExceptionalSupplier<V> task) { public <V> @NotNull Promise<V> thenSupplyAsync(@NotNull ExceptionalSupplier<V> task) {
return thenApplyAsync(result -> task.get()); return thenApplyAsync(_ -> task.get());
} }
@Override @Override
public <V> @NotNull Promise<V> thenSupplyDelayedAsync(@NotNull ExceptionalSupplier<V> task, long delay, @NotNull TimeUnit unit) { public <V> @NotNull Promise<V> thenSupplyDelayedAsync(@NotNull ExceptionalSupplier<V> task, long delay, @NotNull TimeUnit unit) {
return thenApplyDelayedAsync(result -> task.get(), delay, unit); return thenApplyDelayedAsync(_ -> task.get(), delay, unit);
}
@Override
public <V> @NotNull Promise<V> thenApplyAsync(@NotNull ExceptionalFunction<T, V> task) {
CompletablePromise<V> 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;
}
@Override
public <V> @NotNull Promise<V> thenApplyDelayedAsync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit) {
CompletablePromise<V> 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;
}
@Override
public <V> @NotNull Promise<V> thenComposeAsync(@NotNull ExceptionalFunction<T, Promise<V>> task) {
CompletablePromise<V> promise = createLinked();
thenApplyAsync(task).addDirectListener(
nestedPromise -> {
if (nestedPromise == null) {
promise.complete(null);
} else {
PromiseUtil.propagateCompletion(nestedPromise, promise);
PromiseUtil.propagateCancel(promise, nestedPromise);
}
},
promise::completeExceptionally
);
return promise;
} }
@Override @Override
public @NotNull Promise<T> thenPopulateReference(@NotNull AtomicReference<T> reference) { public @NotNull Promise<T> thenPopulateReference(@NotNull AtomicReference<T> reference) {
return thenApplyAsync((result) -> { return thenApply(result -> {
reference.set(result); reference.set(result);
return result; return result;
}); });
} }
@Override
public <V> @NotNull Promise<V> thenApplyAsync(@NotNull ExceptionalFunction<T, V> task) {
Promise<V> promise = getFactory().unresolved();
addDirectListener(
(res) -> {
try {
Runnable runnable = createRunnable(res, promise, task);
F future = getExecutor().runAsync(runnable);
promise.onCancel((e) -> getExecutor().cancel(future));
} catch (RejectedExecutionException e) {
promise.completeExceptionally(e);
}
},
promise::completeExceptionally
);
propagateCancel(promise, this);
return promise;
}
@Override
public <V> @NotNull Promise<V> thenApplyDelayedAsync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit) {
Promise<V> promise = getFactory().unresolved();
addDirectListener(
res -> {
try {
Runnable runnable = createRunnable(res, promise, task);
F future = getExecutor().runAsync(runnable, delay, unit);
promise.onCancel((e) -> getExecutor().cancel(future));
} catch (RejectedExecutionException e) {
promise.completeExceptionally(e);
}
},
promise::completeExceptionally
);
propagateCancel(promise, this);
return promise;
}
@Override
public <V> @NotNull Promise<V> thenComposeAsync(@NotNull ExceptionalFunction<T, Promise<V>> task) {
Promise<V> promise = getFactory().unresolved();
thenApplyAsync(task).addDirectListener(
nestedPromise -> {
if (nestedPromise == null) {
promise.complete(null);
} else {
propagateResult(nestedPromise, promise);
propagateCancel(promise, nestedPromise);
}
},
promise::completeExceptionally
);
propagateCancel(promise, this);
return promise;
}
@Override @Override
public @NotNull Promise<Void> erase() { public @NotNull Promise<Void> erase() {
return thenSupplyAsync(() -> null); return thenSupply(() -> null);
} }
@Override @Override
@@ -377,11 +405,11 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
@Override @Override
public @NotNull Promise<T> addAsyncListener(@Nullable Consumer<T> successListener, @Nullable Consumer<Throwable> errorListener) { public @NotNull Promise<T> addAsyncListener(@Nullable Consumer<T> successListener, @Nullable Consumer<Throwable> errorListener) {
return addAsyncListener((res) -> { return addAsyncListener(res -> {
if (res.isError()) { if (res.isSuccess()) {
if (errorListener != null) errorListener.accept(res.getException());
} else {
if (successListener != null) successListener.accept(res.getResult()); if (successListener != null) successListener.accept(res.getResult());
} else {
if (errorListener != null) errorListener.accept(res.getException());
} }
}); });
} }
@@ -393,54 +421,15 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
@Override @Override
public @NotNull Promise<T> addDirectListener(@Nullable Consumer<T> successListener, @Nullable Consumer<Throwable> errorListener) { public @NotNull Promise<T> addDirectListener(@Nullable Consumer<T> successListener, @Nullable Consumer<Throwable> errorListener) {
return addDirectListener((res) -> { return addDirectListener(res -> {
if (res.isError()) { if (res.isSuccess()) {
if (errorListener != null) errorListener.accept(res.getException());
} else {
if (successListener != null) successListener.accept(res.getResult()); if (successListener != null) successListener.accept(res.getResult());
} else {
if (errorListener != null) errorListener.accept(res.getException());
} }
}); });
} }
private @NotNull Promise<T> addAnyListener(PromiseListener<T> listener) {
PromiseCompletion<T> completion;
lock.lock();
try {
completion = getCompletion();
if (completion == null) {
if (listeners == null) listeners = new LinkedList<>();
listeners.add(listener);
return this;
}
} finally {
lock.unlock();
}
callListener(listener, completion);
return this;
}
private void callListener(PromiseListener<T> listener, PromiseCompletion<T> ctx) {
if (listener instanceof AsyncPromiseListener) {
try {
getExecutor().runAsync(() -> callListenerNow(listener, ctx));
} catch (RejectedExecutionException ignored) {
}
} else {
callListenerNow(listener, ctx);
}
}
private void callListenerNow(PromiseListener<T> listener, PromiseCompletion<T> ctx) {
try {
listener.handle(ctx);
} catch (Exception e) {
getLogger().error("Exception caught in promise listener", e);
}
}
@Override @Override
public @NotNull Promise<T> onSuccess(@NotNull Consumer<T> listener) { public @NotNull Promise<T> onSuccess(@NotNull Consumer<T> listener) {
return addAsyncListener(listener, null); return addAsyncListener(listener, null);
@@ -453,13 +442,14 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
@Override @Override
public @NotNull Promise<T> logExceptions(@NotNull String message) { public @NotNull Promise<T> logExceptions(@NotNull String message) {
return onError(e -> getLogger().error(message, e)); Exception wrapper = new DeferredExecutionException();
return onError(e -> getLogger().error(message, wrapper.initCause(e)));
} }
@Override @Override
public <E extends Throwable> @NotNull Promise<T> onError(@NotNull Class<E> clazz, @NotNull Consumer<E> listener) { public <E extends Throwable> @NotNull Promise<T> onError(@NotNull Class<E> type, @NotNull Consumer<E> listener) {
return onError((e) -> { return onError(e -> {
if (clazz.isAssignableFrom(e.getClass())) { if (type.isAssignableFrom(e.getClass())) {
//noinspection unchecked //noinspection unchecked
listener.accept((E) e); listener.accept((E) e);
} }
@@ -471,79 +461,50 @@ public abstract class AbstractPromise<T, F> implements Promise<T> {
return onError(CancellationException.class, listener); return onError(CancellationException.class, listener);
} }
@Deprecated
@Override @Override
public @NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit) { public @NotNull Promise<T> orDefault(@Nullable T defaultValue) {
return maxWaitTime(time, unit); return orDefault(_ -> defaultValue);
} }
@Override @Override
public @NotNull Promise<T> maxWaitTime(long time, @NotNull TimeUnit unit) { public @NotNull Promise<T> orDefault(@NotNull ExceptionalSupplier<T> supplier) {
try { return orDefault(_ -> supplier.get());
Exception e = new TimeoutException("Promise stopped waiting after " + time + " " + unit);
F future = getExecutor().runAsync(() -> completeExceptionally(e), time, unit);
return addDirectListener((_v) -> getExecutor().cancel(future));
} catch (RejectedExecutionException e) {
completeExceptionally(e);
return this;
}
}
private void handleCompletion(@NotNull PromiseCompletion<T> ctx) {
lock.lock();
try {
if (!setCompletion(ctx)) return;
this.latch.countDown();
if (listeners != null) {
for (PromiseListener<T> listener : listeners) {
callListener(listener, ctx);
}
}
} finally {
lock.unlock();
}
}
private boolean setCompletion(PromiseCompletion<T> completion) {
return this.completion.compareAndSet(null, completion);
} }
@Override @Override
public void cancel(@Nullable String message) { public @NotNull Promise<T> orDefault(@NotNull ExceptionalFunction<Throwable, T> function) {
completeExceptionally(new CancellationException(message)); return useCompletion(
} () -> {
CompletablePromise<T> promise = createLinked();
@Override addDirectListener(promise::complete, e -> runCompleter(promise, () -> promise.complete(function.apply(e))));
public void complete(@Nullable T result) { return promise;
handleCompletion(new PromiseCompletion<>(result)); },
} getFactory()::resolve,
getFactory()::error
@Override );
public void completeExceptionally(@NotNull Throwable result) {
handleCompletion(new PromiseCompletion<>(result));
}
@Override
public boolean isCompleted() {
return completion.get() != null;
}
@Override
public @Nullable PromiseCompletion<T> getCompletion() {
return completion.get();
} }
@Override @Override
public @NotNull CompletableFuture<T> toFuture() { public @NotNull CompletableFuture<T> toFuture() {
return useCompletion(
() -> {
CompletableFuture<T> future = new CompletableFuture<>(); CompletableFuture<T> future = new CompletableFuture<>();
this.addDirectListener(future::complete, future::completeExceptionally); addDirectListener(future::complete, future::completeExceptionally);
future.whenComplete((res, e) -> { future.whenComplete((_, e) -> {
if (e instanceof CancellationException) { if (e instanceof CancellationException) {
this.cancel(); cancel();
} }
}); });
return future; return future;
},
CompletableFuture::completedFuture,
CompletableFuture::failedFuture
);
}
private static class DeferredExecutionException extends ExecutionException {
} }
} }

View File

@@ -1,159 +1,30 @@
package dev.tommyjs.futur.promise; package dev.tommyjs.futur.promise;
import dev.tommyjs.futur.executor.PromiseExecutor; import dev.tommyjs.futur.executor.PromiseExecutor;
import dev.tommyjs.futur.joiner.CompletionJoiner;
import dev.tommyjs.futur.joiner.MappedResultJoiner;
import dev.tommyjs.futur.joiner.ResultJoiner;
import dev.tommyjs.futur.joiner.VoidJoiner;
import dev.tommyjs.futur.util.PromiseUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage; import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public abstract class AbstractPromiseFactory<F> implements PromiseFactory { public abstract class AbstractPromiseFactory<FS, FA> implements PromiseFactory {
public abstract @NotNull PromiseExecutor<F> getExecutor(); public abstract @NotNull Logger getLogger();
public abstract @NotNull PromiseExecutor<FS> getSyncExecutor();
public abstract @NotNull PromiseExecutor<FA> getAsyncExecutor();
@Override @Override
public <K, V> @NotNull Promise<Map.Entry<K, V>> combine(boolean propagateCancel, @NotNull Promise<K> p1, @NotNull Promise<V> p2) { public <T> @NotNull Promise<T> wrap(@NotNull CompletionStage<T> completion, @Nullable Future<T> future) {
List<Promise<?>> promises = List.of(p1, p2); CompletablePromise<T> promise = unresolved();
return all(propagateCancel, promises)
.thenApplyAsync((res) -> new AbstractMap.SimpleImmutableEntry<>(
Objects.requireNonNull(p1.getCompletion()).getResult(),
Objects.requireNonNull(p2.getCompletion()).getResult()
));
}
@Override
public <K, V> @NotNull Promise<Map<K, V>> combine(boolean propagateCancel, @NotNull Map<K, Promise<V>> promises, @Nullable BiConsumer<K, Throwable> exceptionHandler) {
if (promises.isEmpty()) return resolve(Collections.emptyMap());
Map<K, V> map = new HashMap<>();
Promise<Map<K, V>> promise = unresolved();
for (Map.Entry<K, Promise<V>> entry : promises.entrySet()) {
if (propagateCancel) {
AbstractPromise.propagateCancel(promise, entry.getValue());
}
entry.getValue().addDirectListener((ctx) -> {
synchronized (map) {
if (ctx.getException() != null) {
if (exceptionHandler == null) {
promise.completeExceptionally(ctx.getException());
} else {
exceptionHandler.accept(entry.getKey(), ctx.getException());
map.put(entry.getKey(), null);
}
} else {
map.put(entry.getKey(), ctx.getResult());
}
if (map.size() == promises.size()) {
promise.complete(map);
}
}
});
}
return promise;
}
@Override
public <V> @NotNull Promise<List<V>> combine(boolean propagateCancel, @NotNull Iterable<Promise<V>> promises, @Nullable BiConsumer<Integer, Throwable> exceptionHandler) {
AtomicInteger index = new AtomicInteger();
return this.combine(
propagateCancel,
StreamSupport.stream(promises.spliterator(), false)
.collect(Collectors.toMap(k -> index.getAndIncrement(), v -> v)),
exceptionHandler
).thenApplyAsync(v ->
v.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.collect(Collectors.toList())
);
}
@Override
public @NotNull Promise<List<PromiseCompletion<?>>> allSettled(boolean propagateCancel, @NotNull Iterable<Promise<?>> promiseIterable) {
List<Promise<?>> promises = new ArrayList<>();
promiseIterable.iterator().forEachRemaining(promises::add);
if (promises.isEmpty()) return resolve(Collections.emptyList());
PromiseCompletion<?>[] results = new PromiseCompletion<?>[promises.size()];
Promise<List<PromiseCompletion<?>>> promise = unresolved();
var iter = promises.listIterator();
while (iter.hasNext()) {
int index = iter.nextIndex();
var p = iter.next();
if (propagateCancel) {
AbstractPromise.propagateCancel(promise, p);
}
p.addDirectListener((res) -> {
synchronized (results) {
results[index] = res;
if (Arrays.stream(results).allMatch(Objects::nonNull))
promise.complete(Arrays.asList(results));
}
});
}
return promise;
}
@Override
public @NotNull Promise<Void> all(boolean propagateCancel, @NotNull Iterable<Promise<?>> promiseIterable) {
List<Promise<?>> promises = new ArrayList<>();
promiseIterable.iterator().forEachRemaining(promises::add);
if (promises.isEmpty()) return resolve(null);
AtomicInteger completed = new AtomicInteger();
Promise<Void> promise = unresolved();
for (Promise<?> p : promises) {
if (propagateCancel) {
AbstractPromise.propagateCancel(promise, p);
}
p.addDirectListener((res) -> {
if (res.getException() != null) {
promise.completeExceptionally(res.getException());
} else if (completed.incrementAndGet() == promises.size()) {
promise.complete(null);
}
});
}
return promise;
}
@Override
public <V> @NotNull Promise<V> race(boolean cancelRaceLosers, @NotNull Iterable<Promise<V>> promises) {
Promise<V> promise = unresolved();
for (Promise<V> p : promises) {
if (cancelRaceLosers) {
promise.addListener((res) -> p.cancel());
}
AbstractPromise.propagateResult(p, promise);
}
return promise;
}
@Override
public <T> @NotNull Promise<T> wrap(@NotNull CompletableFuture<T> future) {
return wrap(future, future);
}
private <T> @NotNull Promise<T> wrap(@NotNull CompletionStage<T> completion, Future<T> future) {
Promise<T> promise = unresolved();
completion.whenComplete((v, e) -> { completion.whenComplete((v, e) -> {
if (e != null) { if (e != null) {
promise.completeExceptionally(e); promise.completeExceptionally(e);
@@ -162,21 +33,73 @@ public abstract class AbstractPromiseFactory<F> implements PromiseFactory {
} }
}); });
promise.onCancel((e) -> future.cancel(true)); if (future != null) {
promise.onCancel(_ -> future.cancel(true));
}
return promise; return promise;
} }
@Override @Override
public <T> @NotNull Promise<T> resolve(T value) { public <K, V> @NotNull Promise<Map.Entry<K, V>> combine(
Promise<T> promise = unresolved(); @NotNull Promise<K> p1, @NotNull Promise<V> p2
promise.complete(value); ) {
return promise; return all(p1, p2).thenApply(_ -> new AbstractMap.SimpleImmutableEntry<>(
Objects.requireNonNull(p1.getCompletion()).getResult(),
Objects.requireNonNull(p2.getCompletion()).getResult()
));
} }
@Override @Override
public <T> @NotNull Promise<T> error(@NotNull Throwable error) { public @NotNull <K, V> Promise<Map<K, V>> combineMapped(
Promise<T> promise = unresolved(); @NotNull Iterator<Map.Entry<K, Promise<V>>> promises,
promise.completeExceptionally(error); int expectedSize
) {
if (!promises.hasNext()) return resolve(Collections.emptyMap());
return new MappedResultJoiner<>(this, promises, expectedSize).joined();
}
@Override
public <V> @NotNull Promise<List<V>> combine(
@NotNull Iterator<Promise<V>> promises,
int expectedSize
) {
if (!promises.hasNext()) return resolve(Collections.emptyList());
return new ResultJoiner<>(this, promises, expectedSize).joined();
}
@Override
public @NotNull Promise<List<PromiseCompletion<?>>> allSettled(
@NotNull Iterator<Promise<?>> promises,
int expectedSize
) {
if (!promises.hasNext()) return resolve(Collections.emptyList());
return new CompletionJoiner(this, promises, expectedSize).joined();
}
@Override
public @NotNull Promise<Void> all(@NotNull Iterator<Promise<?>> promises) {
if (!promises.hasNext()) return resolve(null);
return new VoidJoiner(this, promises).joined();
}
@Override
public <V> @NotNull Promise<V> race(
@NotNull Iterator<Promise<V>> promises,
boolean ignoreErrors
) {
CompletablePromise<V> promise = unresolved();
while (promises.hasNext()) {
if (promise.isCompleted()) {
promises.forEachRemaining(Promise::cancel);
break;
}
Promise<V> p = promises.next();
PromiseUtil.cancelOnComplete(promise, p);
p.addDirectListener(promise::complete, ignoreErrors ? null : promise::completeExceptionally);
}
return promise; return promise;
} }

View File

@@ -1,5 +1,8 @@
package dev.tommyjs.futur.promise; package dev.tommyjs.futur.promise;
/**
* A listener for a {@link Promise} that is called when the promise is resolved. This listener is
* executed asynchronously by the {@link PromiseFactory} that created the completed promise.
*/
public interface AsyncPromiseListener<T> extends PromiseListener<T> { public interface AsyncPromiseListener<T> extends PromiseListener<T> {
} }

View File

@@ -0,0 +1,193 @@
package dev.tommyjs.futur.promise;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
@SuppressWarnings({"FieldMayBeFinal"})
public abstract class BasePromise<T, FS, FA> extends AbstractPromise<T, FS, FA> implements CompletablePromise<T> {
private static final VarHandle COMPLETION_HANDLE;
private static final VarHandle LISTENERS_HANDLE;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
COMPLETION_HANDLE = lookup.findVarHandle(BasePromise.class, "completion", PromiseCompletion.class);
LISTENERS_HANDLE = lookup.findVarHandle(BasePromise.class, "listeners", Collection.class);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
private final Sync sync;
private volatile PromiseCompletion<T> completion;
@SuppressWarnings("FieldMayBeFinal")
private volatile Collection<PromiseListener<T>> listeners;
@SuppressWarnings("unchecked")
public BasePromise() {
this.sync = new Sync();
this.completion = null;
this.listeners = Collections.EMPTY_LIST;
}
protected void handleCompletion(@NotNull PromiseCompletion<T> cmp) {
if (!COMPLETION_HANDLE.compareAndSet(this, null, cmp)) return;
sync.releaseShared(1);
callListeners(cmp);
}
protected Promise<T> completeExceptionallyDelayed(Throwable e, long delay, TimeUnit unit) {
runCompleter(this, () -> {
FA future = getFactory().getAsyncExecutor().run(() -> completeExceptionally(e), delay, unit);
addDirectListener(_ -> getFactory().getAsyncExecutor().cancel(future));
});
return this;
}
@SuppressWarnings("unchecked")
protected void callListeners(@NotNull PromiseCompletion<T> cmp) {
Iterator<PromiseListener<T>> iter = ((Iterable<PromiseListener<T>>) LISTENERS_HANDLE.getAndSet(this, null)).iterator();
try {
while (iter.hasNext()) {
callListener(iter.next(), cmp);
}
} finally {
iter.forEachRemaining(v -> callListenerAsyncLastResort(v, cmp));
}
}
@Override
protected @NotNull Promise<T> addAnyListener(@NotNull PromiseListener<T> listener) {
Collection<PromiseListener<T>> prev = listeners, next = null;
for (boolean haveNext = false; ; ) {
if (!haveNext) {
next = prev == Collections.EMPTY_LIST ? new ConcurrentLinkedQueue<>() : prev;
if (next != null) next.add(listener);
}
if (LISTENERS_HANDLE.weakCompareAndSet(this, prev, next))
break;
haveNext = (prev == (prev = listeners));
}
if (next == null) {
callListener(listener, Objects.requireNonNull(getCompletion()));
}
return this;
}
@Override
public T get() throws InterruptedException, ExecutionException {
if (!isCompleted()) {
sync.acquireSharedInterruptibly(1);
}
return joinCompletionChecked();
}
@Override
public T get(long time, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (!isCompleted()) {
boolean success = sync.tryAcquireSharedNanos(1, unit.toNanos(time));
if (!success) {
throw new TimeoutException("Promise stopped waiting after " + time + " " + unit);
}
}
return joinCompletionChecked();
}
@Override
public T await() {
if (!isCompleted()) {
try {
sync.acquireSharedInterruptibly(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return joinCompletionUnchecked();
}
@Override
public @NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit) {
Exception e = new CancellationException("Promise timed out after " + time + " " + unit.toString().toLowerCase());
return completeExceptionallyDelayed(e, time, unit);
}
@Override
public @NotNull Promise<T> maxWaitTime(long time, @NotNull TimeUnit unit) {
Exception e = new TimeoutException("Promise stopped waiting after " + time + " " + unit.toString().toLowerCase());
return completeExceptionallyDelayed(e, time, unit);
}
@Override
public void cancel(@NotNull CancellationException e) {
completeExceptionally(e);
}
@Override
public void complete(@Nullable T result) {
handleCompletion(new PromiseCompletion<>(result));
}
@Override
public void completeExceptionally(@NotNull Throwable result) {
handleCompletion(new PromiseCompletion<>(result));
}
@Override
public boolean isCompleted() {
return completion != null;
}
@Override
public @Nullable PromiseCompletion<T> getCompletion() {
return completion;
}
private static final class Sync extends AbstractQueuedSynchronizer {
private Sync() {
setState(1);
}
@Override
protected int tryAcquireShared(int acquires) {
return getState() == 0 ? 1 : -1;
}
@Override
protected boolean tryReleaseShared(int releases) {
int c1, c2;
do {
c1 = getState();
if (c1 == 0) {
return false;
}
c2 = c1 - 1;
} while (!compareAndSetState(c1, c2));
return c2 == 0;
}
}
}

View File

@@ -0,0 +1,25 @@
package dev.tommyjs.futur.promise;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A {@link Promise} that can be completed.
*/
public interface CompletablePromise<T> extends Promise<T> {
/**
* Completes the promise successfully with the given result.
*
* @param result the result
*/
void complete(@Nullable T result);
/**
* Completes the promise exceptionally with the given exception.
*
* @param result the exception
*/
void completeExceptionally(@NotNull Throwable result);
}

View File

@@ -0,0 +1,72 @@
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;
public abstract class CompletedPromise<T, FS, FA> extends AbstractPromise<T, FS, FA> {
private static final PromiseCompletion<?> EMPTY = new PromiseCompletion<>();
private final @NotNull PromiseCompletion<T> completion;
public CompletedPromise(@NotNull PromiseCompletion<T> completion) {
this.completion = completion;
}
@SuppressWarnings("unchecked")
public CompletedPromise() {
this((PromiseCompletion<T>) EMPTY);
}
@Override
protected @NotNull Promise<T> addAnyListener(@NotNull PromiseListener<T> listener) {
callListener(listener, completion);
return this;
}
@Override
public @NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit) {
// Promise is already completed so can't time out
return this;
}
@Override
public @NotNull Promise<T> maxWaitTime(long time, @NotNull TimeUnit unit) {
// Promise is already completed so can't time out
return this;
}
@Override
public void cancel(@NotNull CancellationException exception) {
// Promise is already completed so can't be cancelled
}
@Override
public T get() throws ExecutionException {
return joinCompletionChecked();
}
@Override
public T get(long timeout, @NotNull TimeUnit unit) throws ExecutionException {
return joinCompletionChecked();
}
@Override
public T await() {
return joinCompletionUnchecked();
}
@Override
public @NotNull PromiseCompletion<T> getCompletion() {
return completion;
}
@Override
public boolean isCompleted() {
return true;
}
}

View File

@@ -8,157 +8,603 @@ import org.jetbrains.annotations.Blocking;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CancellationException; import java.util.concurrent.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; import java.util.function.Consumer;
/**
* <p>
* A promise represents the result of an asynchronous computation. A promise will transition from a
* pending state to a completed state at most once, but may remain in a pending state indefinitely.
* </p>
*
* <p>
* Promises are created by a {@link PromiseFactory} and support chaining operations to be executed
* upon completion. These operations can be synchronous or asynchronous, and can be composed in a
* variety of ways. Promises can be listened to for completions, either with a result or with an
* exception. Promises can be cancelled, which will propagate a cancellation signal through the
* chain, but a promise can also be forked, which will prevent propagation of cancellations.
* </p>
*
* @see #cancel()
* @see #fork()
*/
public interface Promise<T> { public interface Promise<T> {
PromiseFactory getFactory(); /**
* Returns the factory that created this promise. This factory can be used to create new promises.
*/
@NotNull PromiseFactory getFactory();
/**
* Chains a task to be executed after this promise completes. The task will be executed immediately
* when 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<Void> thenRun(@NotNull ExceptionalRunnable task); @NotNull Promise<Void> thenRun(@NotNull ExceptionalRunnable task);
/**
* Chains a task to be executed after this promise completes. The task will be executed immediately
* when 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<Void> thenConsume(@NotNull ExceptionalConsumer<T> task); @NotNull Promise<Void> thenConsume(@NotNull ExceptionalConsumer<T> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed immediately
* when 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
*/
<V> @NotNull Promise<V> thenSupply(@NotNull ExceptionalSupplier<V> task); <V> @NotNull Promise<V> thenSupply(@NotNull ExceptionalSupplier<V> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed immediately
* when 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
*/
<V> @NotNull Promise<V> thenApply(@NotNull ExceptionalFunction<T, V> task); <V> @NotNull Promise<V> thenApply(@NotNull ExceptionalFunction<T, V> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed immediately
* when this promise completes, and will compose the next promise in the chainfrom 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
*/
<V> @NotNull Promise<V> thenCompose(@NotNull ExceptionalFunction<T, Promise<V>> task); <V> @NotNull Promise<V> thenCompose(@NotNull ExceptionalFunction<T, Promise<V>> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* sync executor of the factory that created this promise, 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<Void> thenRunSync(@NotNull ExceptionalRunnable task); @NotNull Promise<Void> thenRunSync(@NotNull ExceptionalRunnable task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* sync executor of the factory that created this promise, 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<Void> thenRunDelayedSync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit); @NotNull Promise<Void> thenRunDelayedSync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* sync executor of the factory that created this promise 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<Void> thenConsumeSync(@NotNull ExceptionalConsumer<T> task); @NotNull Promise<Void> thenConsumeSync(@NotNull ExceptionalConsumer<T> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* sync executor of the factory that created this promise 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<Void> thenConsumeDelayedSync(@NotNull ExceptionalConsumer<T> task, long delay, @NotNull TimeUnit unit); @NotNull Promise<Void> thenConsumeDelayedSync(@NotNull ExceptionalConsumer<T> task, long delay, @NotNull TimeUnit unit);
/**
* Chains a task to be executed after this promise completes. The task will be executed immediately
* by the sync executor of the factory that created this promise when 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
*/
<V> @NotNull Promise<V> thenSupplySync(@NotNull ExceptionalSupplier<V> task); <V> @NotNull Promise<V> thenSupplySync(@NotNull ExceptionalSupplier<V> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the sync
* executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenSupplyDelayedSync(@NotNull ExceptionalSupplier<V> task, long delay, @NotNull TimeUnit unit); <V> @NotNull Promise<V> thenSupplyDelayedSync(@NotNull ExceptionalSupplier<V> task, long delay, @NotNull TimeUnit unit);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the sync
* executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenApplySync(@NotNull ExceptionalFunction<T, V> task); <V> @NotNull Promise<V> thenApplySync(@NotNull ExceptionalFunction<T, V> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the sync
* executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenApplyDelayedSync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit); <V> @NotNull Promise<V> thenApplyDelayedSync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the sync
* executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenComposeSync(@NotNull ExceptionalFunction<T, Promise<V>> task); <V> @NotNull Promise<V> thenComposeSync(@NotNull ExceptionalFunction<T, Promise<V>> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* async executor of the factory that created this promise, 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<Void> thenRunAsync(@NotNull ExceptionalRunnable task); @NotNull Promise<Void> thenRunAsync(@NotNull ExceptionalRunnable task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* async executor of the factory that created this promise 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<Void> thenRunDelayedAsync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit); @NotNull Promise<Void> thenRunDelayedAsync(@NotNull ExceptionalRunnable task, long delay, @NotNull TimeUnit unit);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* async executor of the factory that created this promise 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<Void> thenConsumeAsync(@NotNull ExceptionalConsumer<T> task); @NotNull Promise<Void> thenConsumeAsync(@NotNull ExceptionalConsumer<T> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* async executor of the factory that created this promise 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<Void> thenConsumeDelayedAsync(@NotNull ExceptionalConsumer<T> task, long delay, @NotNull TimeUnit unit); @NotNull Promise<Void> thenConsumeDelayedAsync(@NotNull ExceptionalConsumer<T> task, long delay, @NotNull TimeUnit unit);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the
* async executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenSupplyAsync(@NotNull ExceptionalSupplier<V> task); <V> @NotNull Promise<V> thenSupplyAsync(@NotNull ExceptionalSupplier<V> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the async
* executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenSupplyDelayedAsync(@NotNull ExceptionalSupplier<V> task, long delay, @NotNull TimeUnit unit); <V> @NotNull Promise<V> thenSupplyDelayedAsync(@NotNull ExceptionalSupplier<V> task, long delay, @NotNull TimeUnit unit);
@NotNull Promise<T> thenPopulateReference(@NotNull AtomicReference<T> reference); /**
* Chains a task to be executed after this promise completes. The task will be executed by the async
* executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenApplyAsync(@NotNull ExceptionalFunction<T, V> task); <V> @NotNull Promise<V> thenApplyAsync(@NotNull ExceptionalFunction<T, V> task);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the async
* executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenApplyDelayedAsync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit); <V> @NotNull Promise<V> thenApplyDelayedAsync(@NotNull ExceptionalFunction<T, V> task, long delay, @NotNull TimeUnit unit);
/**
* Chains a task to be executed after this promise completes. The task will be executed by the async
* executor of the factory that created this promise 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
*/
<V> @NotNull Promise<V> thenComposeAsync(@NotNull ExceptionalFunction<T, Promise<V>> task); <V> @NotNull Promise<V> thenComposeAsync(@NotNull ExceptionalFunction<T, Promise<V>> 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
* exceptionally.
*
* @param reference the reference to populate
* @return continuation of the promise chain
*/
@NotNull Promise<T> thenPopulateReference(@NotNull AtomicReference<T> reference);
/**
* Returns a promise backed by this promise that will complete with {@code null} if this promise
* completes successfully, or with the exception if this promise completes exceptionally. Cancelling
* the returned promise will cancel this promise, and consequently any previous promises in the chain.
*/
@NotNull Promise<Void> erase(); @NotNull Promise<Void> erase();
/**
* Logs any exceptions that occur in the promise chain with the specified message. The stack trace
* will be captured immediately when invoking this method, and logged alongside an exception if
* encountered, to allow for easier debugging.
*
* @param message the message to log
* @return continuation of the promise chain
*/
@NotNull Promise<T> logExceptions(@NotNull String message);
/**
* Logs any exceptions that occur in the promise chain. The stack trace will be captured immediately
* when invoking this method, and logged alongside an exception if encountered, to allow for easier
* debugging.
*
* @return continuation of the promise chain
*/
default @NotNull Promise<T> logExceptions() { default @NotNull Promise<T> logExceptions() {
return logExceptions("Exception caught in promise chain"); return logExceptions("Exception caught in promise chain");
} }
@NotNull Promise<T> logExceptions(@NotNull String message);
/** /**
* @apiNote Direct listeners run on the same thread as the completion. * Adds a listener to this promise that will be executed immediately when this promise completes,
* on the same thread as the completion call.
*
* @param listener the listener to add
* @return continuation of the promise chain
*/ */
@NotNull Promise<T> addDirectListener(@NotNull PromiseListener<T> listener); @NotNull Promise<T> addDirectListener(@NotNull PromiseListener<T> listener);
/**
* Adds a listener to this promise that will be executed immediately when this promise completes,
* on the same thread as the completion call. One of {@code successHandler} and {@code errorHandler} will be
* called when the promise completes successfully or exceptionally, respectively.
*
* @param successHandler the function to call on success
* @param errorHandler the function to call on error
* @return continuation of the promise chain
*/
@NotNull Promise<T> addDirectListener(@Nullable Consumer<T> successHandler, @Nullable Consumer<Throwable> errorHandler); @NotNull Promise<T> addDirectListener(@Nullable Consumer<T> successHandler, @Nullable Consumer<Throwable> errorHandler);
/** /**
* @apiNote Async listeners are run in parallel. * Adds a listener to this promise that will be executed immediately when this promise completes,
* by the async executor of the factory that created this promise.
*
* @param listener the listener to add
* @return continuation of the promise chain
*/ */
@NotNull Promise<T> addAsyncListener(@NotNull AsyncPromiseListener<T> listener); @NotNull Promise<T> addAsyncListener(@NotNull AsyncPromiseListener<T> listener);
/** /**
* @apiNote Same as addAsyncListener. * Adds a listener to this promise that will be executed immediately when this promise completes.
*
* @param listener the listener to add
* @return continuation of the promise chain
*/ */
default @NotNull Promise<T> addListener(@NotNull AsyncPromiseListener<T> listener) { default @NotNull Promise<T> addListener(@NotNull AsyncPromiseListener<T> listener) {
return addAsyncListener(listener); return addAsyncListener(listener);
} }
/**
* Adds a listener to this promise that will be executed immediately when this promise completes,
* by the async executor of the factory that created this promise. One of {@code successHandler} and
* {@code errorHandler} will be called when the promise completes successfully or exceptionally, respectively.
*
* @param successHandler the function to call on success
* @param errorHandler the function to call on error
*/
@NotNull Promise<T> addAsyncListener(@Nullable Consumer<T> successHandler, @Nullable Consumer<Throwable> errorHandler); @NotNull Promise<T> addAsyncListener(@Nullable Consumer<T> successHandler, @Nullable Consumer<Throwable> errorHandler);
/**
* Adds a listener to this promise that will be called if the promise is completed successfully.
*
* @param listener the listener to add
* @return continuation of the promise chain
*/
@NotNull Promise<T> onSuccess(@NotNull Consumer<T> listener); @NotNull Promise<T> onSuccess(@NotNull Consumer<T> listener);
/**
* Adds a listener to this promise that will be called if the promise is completed exceptionally.
*
* @param listener the listener to add
* @return continuation of the promise chain
*/
@NotNull Promise<T> onError(@NotNull Consumer<Throwable> listener); @NotNull Promise<T> onError(@NotNull Consumer<Throwable> listener);
<E extends Throwable> @NotNull Promise<T> onError(@NotNull Class<E> clazz, @NotNull Consumer<E> listener); /**
* Adds a listener to this promise that will be called if the promise is completed exceptionally
* with an exception of the specified type.
*
* @param listener the listener to add
* @param type the class of the exception to listen for
* @return continuation of the promise chain
*/
<E extends Throwable> @NotNull Promise<T> onError(@NotNull Class<E> type, @NotNull Consumer<E> listener);
/**
* Adds a listener to this promise that will be called if the promise is cancelled.
*
* @param listener the listener to add
* @return continuation of the promise chain
*/
@NotNull Promise<T> onCancel(@NotNull Consumer<CancellationException> listener); @NotNull Promise<T> onCancel(@NotNull Consumer<CancellationException> listener);
/** /**
* @deprecated Use maxWaitTime instead * Creates a new promise that will always complete successfully - either with the result of this
* promise, or with the specified default value if this promise completes exceptionally. Cancelling
* the returned promise will cancel this promise, and consequently any previous promises in the chain.
*
* @param defaultValue the default value to complete the promise with if this promise completes exceptionally
* @return a new promise that completes with the result of this promise, or with the default value if this
* promise completes exceptionally
*/
@NotNull Promise<T> orDefault(@Nullable T defaultValue);
/**
* Creates a new promise that will attempt to always complete successfully - either with the result
* of this promise, or with the result of the specified supplier if this promise completes exceptionally.
* If an exception is encountered while executing the supplier, the promise will complete exceptionally
* with that exception. Cancelling the returned promise will cancel this promise, and consequently any
* previous promises in the chain.
*
* @param supplier the supplier to complete the promise with if this promise completes exceptionally
* @return a new promise that completes with the result of this promise, or with the result of the
* supplier if this promise completes exceptionally
*/
@NotNull Promise<T> orDefault(@NotNull ExceptionalSupplier<T> supplier);
/**
* Creates a new promise that will attempt to always complete successfully - either with the result
* of this promise, or with the result of the specified function if this promise completes
* exceptionally. If an exception is encountered while executing the function, the promise will
* complete exceptionally with that exception. Cancelling the returned promise will cancel this
* promise, and consequently any previous promises in the chain.
*
* @param function the function to complete the promise with if this promise completes exceptionally
* @return a new promise that completes with the result of this promise, or with the result of the
* function if this promise completes exceptionally
*/
@NotNull Promise<T> orDefault(@NotNull ExceptionalFunction<Throwable, T> function);
/**
* Cancels the promise if not already completed after the specified timeout. This will result in
* an exceptional completion with a {@link CancellationException}.
*
* @param time the amount of time to wait before cancelling the promise
* @param unit the time unit of the delay
* @return continuation of the promise chain
*/ */
@Deprecated
@NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit); @NotNull Promise<T> timeout(long time, @NotNull TimeUnit unit);
/** /**
* @deprecated Use maxWaitTime instead * Cancels the promise if not already completed after the specified timeout. This will result in
* an exceptional completion with a {@link CancellationException}.
*
* @param ms the amount of time to wait before cancelling the promise (in milliseconds)
* @return continuation of the promise chain
*/ */
@Deprecated
default @NotNull Promise<T> timeout(long ms) { default @NotNull Promise<T> timeout(long ms) {
return timeout(ms, TimeUnit.MILLISECONDS); return timeout(ms, TimeUnit.MILLISECONDS);
} }
/**
* Times out the promise if not already completed after the specified timeout. This will result
* in an exceptional completion with a {@link TimeoutException}. This will not result in the
* promise being cancelled.
*
* @param time the amount of time to wait before timing out the promise
* @param unit the time unit of the delay
* @return continuation of the promise chain
*/
@NotNull Promise<T> maxWaitTime(long time, @NotNull TimeUnit unit); @NotNull Promise<T> maxWaitTime(long time, @NotNull TimeUnit unit);
/**
* Times out the promise if not already completed after the specified timeout. This will result
* in an exceptional completion with a {@link TimeoutException}. This will not result in the
* promise being cancelled.
*
* @param ms the amount of time to wait before timing out the promise (in milliseconds)
* @return continuation of the promise chain
*/
default @NotNull Promise<T> maxWaitTime(long ms) { default @NotNull Promise<T> maxWaitTime(long ms) {
return maxWaitTime(ms, TimeUnit.MILLISECONDS); return maxWaitTime(ms, TimeUnit.MILLISECONDS);
} }
void cancel(@Nullable String reason); /**
* Cancels the promise if not already completed after the specified timeout. This will result in
* an exceptional completion with the specified cancellation.
*
* @param exception the cancellation exception to complete the promise with
*/
void cancel(@NotNull CancellationException exception);
default void cancel() { /**
cancel(null); * Cancels the promise if not already completed after the specified timeout. This will result in
* an exceptional completion with a {@link CancellationException}.
*
* @param reason the reason for the cancellation
*/
default void cancel(@NotNull String reason) {
cancel(new CancellationException(reason));
} }
void complete(@Nullable T result); /**
* Cancels the promise if not already completed after the specified timeout. This will result in
void completeExceptionally(@NotNull Throwable result); * an exceptional completion with a {@link CancellationException}.
*/
default void cancel() {
cancel(new CancellationException());
}
/**
* Blocks until this promise has completed, and then returns its result. This method will throw
* checked exceptions if the promise completes exceptionally or the thread is interrupted.
*
* @return the result of the promise
* @throws CancellationException if the promise was cancelled
* @throws ExecutionException if the promise completed exceptionally
* @throws InterruptedException if the current thread was interrupted while waiting
*/
@Blocking @Blocking
T awaitInterruptibly() throws InterruptedException; T get() throws InterruptedException, ExecutionException;
/**
* Blocks until either this promise has completed or the timeout has been exceeded, and then
* returns its result, if available. This method will throw checked exceptions if the promise
* completes exceptionally or the thread is interrupted, or the timeout is exceeded.
*
* @return the result of the promise
* @throws CancellationException if the promise was cancelled
* @throws ExecutionException if the promise completed exceptionally
* @throws InterruptedException if the current thread was interrupted while waiting
* @throws TimeoutException if the timeout was exceeded
*/
@Blocking @Blocking
T awaitInterruptibly(long timeout) throws TimeoutException, InterruptedException; T get(long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
/**
* Blocks until this promise has completed, and then returns its result. This method is similar
* to {@link #get()}, but will throw unchecked exceptions instead of checked exceptions if the
* promise completes exceptionally or the thread is interrupted.
*
* @return the result of the promise
* @throws CancellationException if the promise was cancelled
* @throws CompletionException if the promise completed exceptionally
*/
@Blocking @Blocking
T await(); T await();
@Blocking /**
T await(long timeout) throws TimeoutException; * Returns a new promise, backed by this promise, that will not propagate cancellations. This means
* that if the returned promise is cancelled, the cancellation will not be propagated to this promise,
* and consequently any previous promises in the chain.
*
* @return continuation the promise chain that will not propagate cancellations
*/
@NotNull Promise<T> fork();
/** /**
* @deprecated Use await instead. * Returns the current completion state of this promise. If the promise has not completed, this method
* will return {@code null}.
*
* @return the completion state of this promise, or {@code null} if the promise has not completed
*/ */
@Blocking
@Deprecated
default T join(long timeout) throws TimeoutException {
return await(timeout);
};
@Nullable PromiseCompletion<T> getCompletion(); @Nullable PromiseCompletion<T> getCompletion();
/**
* Returns whether this promise has completed.
*
* @return {@code true} if the promise has completed, {@code false} otherwise
*/
boolean isCompleted(); boolean isCompleted();
/**
* Converts this promise to a {@link CompletableFuture}. The returned future will complete with the
* result of this promise when it completes.
*
* @return a future that will complete with the result of this promise
*/
@NotNull CompletableFuture<T> toFuture(); @NotNull CompletableFuture<T> toFuture();
} }

View File

@@ -5,35 +5,85 @@ import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
/**
* Represents the result of a {@link Promise}, containing either an optional result or an exception.
*/
public class PromiseCompletion<T> { public class PromiseCompletion<T> {
private @Nullable T result; private @Nullable T result;
private @Nullable Throwable exception; private @Nullable Throwable exception;
/**
* Creates a new successful completion.
*
* @param result the result
*/
public PromiseCompletion(@Nullable T result) { public PromiseCompletion(@Nullable T result) {
this.result = result; this.result = result;
} }
/**
* Creates a new exceptional completion.
*
* @param exception the exception
*/
public PromiseCompletion(@NotNull Throwable exception) { public PromiseCompletion(@NotNull Throwable exception) {
this.exception = exception; this.exception = exception;
} }
/**
* Creates a new successful completion with a result of {@code null}.
*/
public PromiseCompletion() { public PromiseCompletion() {
this.result = null; this((T) null);
} }
/**
* Checks if the completion was successful.
*
* @return {@code true} if the completion was successful, {@code false} otherwise
*/
public boolean isSuccess() {
return exception == null;
}
/**
* Checks if the completion was exceptional.
*
* @return {@code true} if the completion was exceptional, {@code false} otherwise
*/
public boolean isError() { public boolean isError() {
return getException() != null; return exception != null;
} }
/**
* Checks if the completion was cancelled.
*
* @return {@code true} if the completion was cancelled, {@code false} otherwise
*/
public boolean wasCancelled() {
return exception instanceof CancellationException;
}
@Deprecated
public boolean wasCanceled() { public boolean wasCanceled() {
return getException() instanceof CancellationException; return wasCancelled();
} }
/**
* Gets the result of the completion.
*
* @return the result, or {@code null} if the completion was exceptional
*/
public @Nullable T getResult() { public @Nullable T getResult() {
return result; return result;
} }
/**
* Gets the exception of the completion.
*
* @return the exception, or {@code null} if the completion was successful
*/
public @Nullable Throwable getException() { public @Nullable Throwable getException() {
return exception; return exception;
} }

View File

@@ -1,100 +1,502 @@
package dev.tommyjs.futur.promise; package dev.tommyjs.futur.promise;
import dev.tommyjs.futur.executor.PromiseExecutor;
import dev.tommyjs.futur.util.PromiseUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.util.Arrays; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer; import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* A factory for creating and combining promises.
*/
@SuppressWarnings("unchecked")
public interface PromiseFactory { public interface PromiseFactory {
@NotNull Logger getLogger(); /**
* Creates a new {@link PromiseFactory} with the given logger and executors.
<T> @NotNull Promise<T> unresolved(); *
* @param logger the logger
<K, V> @NotNull Promise<Map.Entry<K, V>> combine(boolean propagateCancel, @NotNull Promise<K> p1, @NotNull Promise<V> p2); * @param syncExecutor the synchronous executor
* @param asyncExecutor the asynchronous executor
default <K, V> @NotNull Promise<Map.Entry<K, V>> combine(@NotNull Promise<K> p1, @NotNull Promise<V> p2) { * @return the new promise factory
return combine(false, p1, p2); */
} static @NotNull PromiseFactory of(@NotNull Logger logger, @NotNull PromiseExecutor<?> syncExecutor,
@NotNull PromiseExecutor<?> asyncExecutor) {
<K, V> @NotNull Promise<Map<K, V>> combine(boolean propagateCancel, @NotNull Map<K, Promise<V>> promises, @Nullable BiConsumer<K, Throwable> exceptionHandler); return new PromiseFactoryImpl<>(logger, syncExecutor, asyncExecutor);
default <K, V> @NotNull Promise<Map<K, V>> combine(boolean propagateCancel, @NotNull Map<K, Promise<V>> promises) {
return combine(propagateCancel, promises, null);
}
default <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, @Nullable BiConsumer<K, Throwable> exceptionHandler) {
return combine(false, promises, exceptionHandler);
}
default <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises) {
return combine(promises, null);
}
<V> @NotNull Promise<List<V>> combine(boolean propagateCancel, @NotNull Iterable<Promise<V>> promises, @Nullable BiConsumer<Integer, Throwable> exceptionHandler);
default <V> @NotNull Promise<List<V>> combine(boolean propagateCancel, @NotNull Iterable<Promise<V>> promises) {
return combine(propagateCancel, promises, null);
}
default <V> @NotNull Promise<List<V>> combine(@NotNull Iterable<Promise<V>> promises, @Nullable BiConsumer<Integer, Throwable> exceptionHandler) {
return combine(false, promises, exceptionHandler);
}
default <V> @NotNull Promise<List<V>> combine(@NotNull Iterable<Promise<V>> promises) {
return combine(promises, null);
}
@NotNull Promise<List<PromiseCompletion<?>>> allSettled(boolean propagateCancel, @NotNull Iterable<Promise<?>> promiseIterable);
default @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Iterable<Promise<?>> promiseIterable) {
return allSettled(false, promiseIterable);
}
default @NotNull Promise<List<PromiseCompletion<?>>> allSettled(boolean propagateCancel, @NotNull Promise<?>... promiseArray) {
return allSettled(propagateCancel, Arrays.asList(promiseArray));
}
default @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Promise<?>... promiseArray) {
return allSettled(false, promiseArray);
}
@NotNull Promise<Void> all(boolean propagateCancel, @NotNull Iterable<Promise<?>> promiseIterable);
default @NotNull Promise<Void> all(@NotNull Iterable<Promise<?>> promiseIterable) {
return all(false, promiseIterable);
}
default @NotNull Promise<Void> all(boolean propagateCancel, @NotNull Promise<?>... promiseArray) {
return all(propagateCancel, Arrays.asList(promiseArray));
}
default @NotNull Promise<Void> all(@NotNull Promise<?>... promiseArray) {
return all(false, promiseArray);
} }
/** /**
* @apiNote Even with cancelRaceLosers, it is not guaranteed that only one promise will complete. * Creates a new {@link PromiseFactory} with the given logger and dual executor.
*
* @param logger the logger
* @param executor the executor
* @return the new promise factory
*/ */
<V> @NotNull Promise<V> race(boolean cancelRaceLosers, @NotNull Iterable<Promise<V>> promises); static @NotNull PromiseFactory of(@NotNull Logger logger, @NotNull PromiseExecutor<?> executor) {
return new PromiseFactoryImpl<>(logger, executor, executor);
}
/**
* Creates a new {@link PromiseFactory} with the given logger and executor.
*
* @param logger the logger
* @param executor the executor
* @return the new promise factory
*/
static @NotNull PromiseFactory of(@NotNull Logger logger, @NotNull ScheduledExecutorService executor) {
return of(logger, PromiseExecutor.of(executor));
}
/**
* Creates a new uncompleted promise.
*
* @return the new promise
*/
<T> @NotNull CompletablePromise<T> unresolved();
/**
* Creates a new promise, completed with the given value.
*
* @param value the value to complete the promise with
* @return the new promise
*/
<T> @NotNull Promise<T> resolve(T value);
/**
* Creates a new promise, completed with {@code null}. This method is often useful for starting
* promise chains.
*
* @return the new promise
*/
@NotNull Promise<Void> start();
/**
* Creates a new promise, completed exceptionally with the given error.
*
* @param error the error to complete the promise with
* @return the new promise
*/
<T> @NotNull Promise<T> error(@NotNull Throwable error);
/**
* Creates a new promise backed by the given completion and future.
* The promise will be completed upon completion of the {@link CompletionStage}
* and the {@link Future} will be cancelled upon cancellation of the promise.
*
* @param completion the completion stage to wrap
* @param future the future to wrap
* @return the new promise
*/
<T> @NotNull Promise<T> wrap(@NotNull CompletionStage<T> completion, @Nullable Future<T> future);
/**
* Creates a new promise backed by the given future.
* The promise will be completed upon completion of the {@link CompletableFuture}
* and the {@link CompletableFuture} will be cancelled upon cancellation of the promise.
*
* @param future the future to wrap
* @return the new promise
*/
default <T> @NotNull Promise<T> wrap(@NotNull CompletableFuture<T> future) {
return wrap(future, future);
};
/**
* Combines two promises into a single promise that resolves when both promises are completed.
* If either input promise completes exceptionally, the other promise will be cancelled
* and the output promise will complete exceptionally.
*
* @param p1 the first promise
* @param p2 the second promise
* @return the combined promise
*/
<K, V> @NotNull Promise<Map.Entry<K, V>> combine(@NotNull Promise<K> p1, @NotNull Promise<V> p2);
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @param expectedSize the expected size of the iterator (used for optimization)
* @return the combined promise
*/
<K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterator<Map.Entry<K, Promise<V>>> promises,
int expectedSize);
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Spliterator<Map.Entry<K, Promise<V>>> promises) {
return combineMapped(Spliterators.iterator(promises), PromiseUtil.estimateSize(promises));
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Stream<Map.Entry<K, Promise<V>>> promises) {
return combineMapped(promises.spliterator());
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterable<Map.Entry<K, Promise<V>>> promises) {
return combineMapped(promises.spliterator());
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Map.Entry<K, Promise<V>>... promises) {
return combineMapped(Arrays.spliterator(promises));
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Map<K, Promise<V>> promises) {
return combineMapped(promises.entrySet().iterator(), promises.size());
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param keys the keys to map to promises
* @param mapper the function to map keys to promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Stream<K> keys,
@NotNull Function<K, Promise<V>> mapper) {
return combineMapped(keys.map(k -> new AbstractMap.SimpleImmutableEntry<>(k, mapper.apply(k))));
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param keys the keys to map to promises
* @param mapper the function to map keys to promises
* @return the combined promise
*/
default <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterable<K> keys,
@NotNull Function<K, Promise<V>> mapper) {
return combineMapped(StreamSupport.stream(keys.spliterator(), false), mapper);
}
/**
* @deprecated Use combineMapped instead.
*/
@Deprecated
default <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises) {
return combineMapped(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @param expectedSize the expected size of the iterator (used for optimization)
* @return the combined promise
*/
<V> @NotNull Promise<List<V>> combine(@NotNull Iterator<Promise<V>> promises, int expectedSize);
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Spliterator<Promise<V>> promises) {
return combine(Spliterators.iterator(promises), PromiseUtil.estimateSize(promises));
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Stream<Promise<V>> promises) {
return combine(promises.spliterator());
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Iterable<Promise<V>> promises) {
return combine(promises.spliterator());
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default <V> @NotNull Promise<List<V>> combine(@NotNull Promise<V>... promises) {
return combine(Arrays.spliterator(promises));
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @param expectedSize the expected size of the iterator (used for optimization)
* @return the combined promise
*/
@NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Iterator<Promise<?>> promises,
int expectedSize);
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @return the combined promise
*/
default @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Spliterator<Promise<?>> promises) {
return allSettled(Spliterators.iterator(promises), PromiseUtil.estimateSize(promises));
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @return the combined promise
*/
default @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Stream<Promise<?>> promises) {
return allSettled(promises.spliterator());
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @return the combined promise
*/
default @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Iterable<Promise<?>> promises) {
return allSettled(promises.spliterator());
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @return the combined promise
*/
default @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Promise<?>... promises) {
return allSettled(Arrays.spliterator(promises));
}
/**
* Combines multiple promises into a single promise that completes when all promises complete.
* If any promise completes exceptionally, all other promises will be cancelled
* and the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
@NotNull Promise<Void> all(@NotNull Iterator<Promise<?>> promises);
/**
* Combines multiple promises into a single promise that completes when all promises complete.
* If any promise completes exceptionally, all other promises will be cancelled
* and the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default @NotNull Promise<Void> all(@NotNull Stream<Promise<?>> promises) {
return all(promises.iterator());
}
/**
* Combines multiple promises into a single promise that completes when all promises complete.
* If any promise completes exceptionally, all other promises will be cancelled
* and the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default @NotNull Promise<Void> all(@NotNull Iterable<Promise<?>> promises) {
return all(promises.iterator());
}
/**
* Combines multiple promises into a single promise that completes when all promises complete.
* If any promise completes exceptionally, all other promises will be cancelled
* and the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
default @NotNull Promise<Void> all(@NotNull Promise<?>... promises) {
return all(Arrays.asList(promises).iterator());
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If {@code ignoreErrors} is {@code false} and the first promise completed exceptionally, the
* combined promise will also complete exceptionally. Otherwise, the combined promise will wait for a
* successful completion or complete with {@code null} if all promises complete exceptionally.
* Additionally, if {@code cancelLosers} is {@code true}, the other promises will be cancelled
* once the combined promise is completed.
*
* @param promises the input promises
* @param ignoreErrors whether to ignore promises that complete exceptionally
* @return the combined promise
*/
<V> @NotNull Promise<V> race(@NotNull Iterator<Promise<V>> promises, boolean ignoreErrors);
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If {@code ignoreErrors} is {@code false} and the first promise completed exceptionally, the
* combined promise will also complete exceptionally. Otherwise, the combined promise will wait for a
* successful completion or complete with {@code null} if all promises complete exceptionally.
* Additionally, The other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @param ignoreErrors whether to ignore promises that complete exceptionally
* @return the combined promise
*/
default <V> @NotNull Promise<V> race(@NotNull Stream<Promise<V>> promises, boolean ignoreErrors) {
return race(promises.iterator(), ignoreErrors);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If the first promise completed exceptionally, the combined promise will also complete exceptionally.
* Additionally, the other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @return the combined promise
*/
default <V> @NotNull Promise<V> race(@NotNull Stream<Promise<V>> promises) {
return race(promises, false);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If {@code ignoreErrors} is {@code false} and the first promise completed exceptionally, the
* combined promise will also complete exceptionally. Otherwise, the combined promise will wait for a
* successful completion or complete with {@code null} if all promises complete exceptionally.
* Additionally, The other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @param ignoreErrors whether to ignore promises that complete exceptionally
* @return the combined promise
*/
default <V> @NotNull Promise<V> race(@NotNull Iterable<Promise<V>> promises, boolean ignoreErrors) {
return race(promises.iterator(), ignoreErrors);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If the first promise completed exceptionally, the combined promise will also complete exceptionally.
* Additionally, the other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @return the combined promise
*/
default <V> @NotNull Promise<V> race(@NotNull Iterable<Promise<V>> promises) { default <V> @NotNull Promise<V> race(@NotNull Iterable<Promise<V>> promises) {
return race(promises, false);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If {@code ignoreErrors} is {@code false} and the first promise completed exceptionally, the
* combined promise will also complete exceptionally. Otherwise, the combined promise will wait for a
* successful completion or complete with {@code null} if all promises complete exceptionally.
* Additionally, The other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @param ignoreErrors whether to ignore promises that complete exceptionally
* @return the combined promise
*/
default <V> @NotNull Promise<V> race(boolean ignoreErrors, @NotNull Promise<V>... promises) {
return race(Arrays.asList(promises), ignoreErrors);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If the first promise completed exceptionally, the combined promise will also complete exceptionally.
* Additionally, the other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @return the combined promise
*/
default <V> @NotNull Promise<V> race(@NotNull Promise<V>... promises) {
return race(false, promises); return race(false, promises);
} }
<T> @NotNull Promise<T> wrap(@NotNull CompletableFuture<T> future);
default @NotNull Promise<Void> start() {
return resolve(null);
}
<T> @NotNull Promise<T> resolve(T value);
<T> @NotNull Promise<T> error(@NotNull Throwable error);
} }

View File

@@ -0,0 +1,89 @@
package dev.tommyjs.futur.promise;
import dev.tommyjs.futur.executor.PromiseExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
public class PromiseFactoryImpl<FS, FA> extends AbstractPromiseFactory<FS, FA> {
private final @NotNull Logger logger;
private final @NotNull PromiseExecutor<FS> syncExecutor;
private final @NotNull PromiseExecutor<FA> asyncExecutor;
public PromiseFactoryImpl(
@NotNull Logger logger,
@NotNull PromiseExecutor<FS> syncExecutor,
@NotNull PromiseExecutor<FA> asyncExecutor
) {
this.logger = logger;
this.syncExecutor = syncExecutor;
this.asyncExecutor = asyncExecutor;
}
@Override
public @NotNull <T> CompletablePromise<T> unresolved() {
return new PromiseImpl<>();
}
@Override
public @NotNull <T> Promise<T> resolve(T value) {
return new CompletedPromiseImpl<>(value);
}
@Override
public @NotNull Promise<Void> start() {
return new CompletedPromiseImpl<>();
}
@Override
public @NotNull <T> Promise<T> error(@NotNull Throwable error) {
return new CompletedPromiseImpl<>(error);
}
@Override
public @NotNull Logger getLogger() {
return logger;
}
@Override
public @NotNull PromiseExecutor<FS> getSyncExecutor() {
return syncExecutor;
}
@Override
public @NotNull PromiseExecutor<FA> getAsyncExecutor() {
return asyncExecutor;
}
private class PromiseImpl<T> extends BasePromise<T, FS, FA> {
@Override
public @NotNull AbstractPromiseFactory<FS, FA> getFactory() {
return PromiseFactoryImpl.this;
}
}
private class CompletedPromiseImpl<T> extends CompletedPromise<T, FS, FA> {
public CompletedPromiseImpl(@Nullable T result) {
super(new PromiseCompletion<>(result));
}
public CompletedPromiseImpl(@NotNull Throwable exception) {
super(new PromiseCompletion<>(exception));
}
public CompletedPromiseImpl() {
super();
}
@Override
public @NotNull AbstractPromiseFactory<FS, FA> getFactory() {
return PromiseFactoryImpl.this;
}
}
}

View File

@@ -2,8 +2,16 @@ package dev.tommyjs.futur.promise;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/**
* A listener for a {@link Promise} that is called when the promise is resolved.
*/
public interface PromiseListener<T> { public interface PromiseListener<T> {
void handle(@NotNull PromiseCompletion<T> ctx); /**
* Handles the completion of the promise.
*
* @param completion the promise completion
*/
void handle(@NotNull PromiseCompletion<T> completion);
} }

View File

@@ -1,90 +0,0 @@
package dev.tommyjs.futur.promise;
import dev.tommyjs.futur.function.ExceptionalFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
/**
* @deprecated Use PromiseFactory instance methods instead.
*/
@Deprecated
public class Promises {
public static <K, V> @NotNull Promise<Map.Entry<K, V>> combine(@NotNull Promise<K> p1, @NotNull Promise<V> p2) {
return combine(p1, p2, p1.getFactory());
}
public static <K, V> @NotNull Promise<Map.Entry<K, V>> combine(@NotNull Promise<K> p1, @NotNull Promise<V> p2, PromiseFactory factory) {
return factory.combine(p1, p2);
}
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, long timeout, PromiseFactory factory) {
return combine(promises, timeout, true, factory);
}
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, long timeout, boolean strict, PromiseFactory factory) {
return combine(promises, timeout, strict ? null : (_k, _v) -> {}, factory);
}
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, long timeout, @Nullable BiConsumer<K, Throwable> exceptionHandler, PromiseFactory factory) {
return factory.combine(promises, exceptionHandler).timeout(timeout);
}
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, PromiseFactory factory) {
return combine(promises, 1500L, true, factory);
}
public static <V> @NotNull Promise<List<V>> combine(@NotNull List<Promise<V>> promises, long timeout, PromiseFactory factory) {
return combine(promises, timeout, true, factory);
}
public static <V> @NotNull Promise<List<V>> combine(@NotNull List<Promise<V>> promises, long timeout, boolean strict, PromiseFactory factory) {
return factory.combine(promises, strict ? null : (_i, _v) -> {}).timeout(timeout);
}
public static <V> @NotNull Promise<List<V>> combine(@NotNull List<Promise<V>> promises, PromiseFactory factory) {
return combine(promises, 1500L, true, factory);
}
public static @NotNull Promise<Void> all(@NotNull List<Promise<?>> promises, PromiseFactory factory) {
return factory.all(promises);
}
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Collection<K> keys, @NotNull ExceptionalFunction<K, V> mapper, long timeout, PromiseFactory factory) {
return combine(keys, mapper, timeout, true, factory);
}
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Collection<K> keys, @NotNull ExceptionalFunction<K, V> mapper, long timeout, boolean strict, PromiseFactory factory) {
Map<K, Promise<V>> promises = new HashMap<>();
for (K key : keys) {
Promise<V> promise = factory.resolve(key).thenApplyAsync(mapper);
promises.put(key, promise);
}
return combine(promises, timeout, strict, factory);
}
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Collection<K> keys, @NotNull ExceptionalFunction<K, V> mapper, PromiseFactory factory) {
return combine(keys, mapper, 1500L, true, factory);
}
public static @NotNull Promise<Void> erase(@NotNull Promise<?> p) {
return erase(p, p.getFactory());
}
public static @NotNull Promise<Void> erase(@NotNull Promise<?> p, PromiseFactory factory) {
return p.erase();
}
public static <T> @NotNull Promise<T> wrap(@NotNull CompletableFuture<T> future, PromiseFactory factory) {
return factory.wrap(future);
}
}

View File

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

View File

@@ -0,0 +1,52 @@
package dev.tommyjs.futur.util;
import dev.tommyjs.futur.promise.CompletablePromise;
import dev.tommyjs.futur.promise.Promise;
import org.jetbrains.annotations.NotNull;
import java.util.Spliterator;
public class PromiseUtil {
/**
* Propagates the completion, once completed, of the given promise to the given promise.
*
* @param from the promise to propagate the completion from
* @param to the completable promise to propagate the completion to
*/
public static <V> void propagateCompletion(@NotNull Promise<V> from, @NotNull CompletablePromise<V> to) {
from.addDirectListener(to::complete, to::completeExceptionally);
}
/**
* Propagates the cancellation, once cancelled, of the given promise to the given promise.
*
* @param from the promise to propagate the cancellation from
* @param to the promise to propagate the cancellation to
*/
public static void propagateCancel(@NotNull Promise<?> from, @NotNull Promise<?> to) {
from.onCancel(to::cancel);
}
/**
* Cancels the given promise once the given promise is completed.
*
* @param from the promise to propagate the completion from
* @param to the promise to cancel upon completion
*/
public static void cancelOnComplete(@NotNull Promise<?> from, @NotNull Promise<?> to) {
from.addDirectListener(_ -> to.cancel());
}
/**
* Estimates the size of the given stream.
*
* @param stream the stream
* @return the estimated size
*/
public static int estimateSize(@NotNull Spliterator<?> stream) {
long estimate = stream.estimateSize();
return estimate == Long.MAX_VALUE ? 10 : (int) estimate;
}
}

View File

@@ -1,7 +1,7 @@
package dev.tommyjs.futur; package dev.tommyjs.futur;
import dev.tommyjs.futur.executor.SinglePoolExecutor; import dev.tommyjs.futur.promise.CompletablePromise;
import dev.tommyjs.futur.impl.SimplePromiseFactory; import dev.tommyjs.futur.promise.CompletedPromise;
import dev.tommyjs.futur.promise.Promise; import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseFactory; import dev.tommyjs.futur.promise.PromiseFactory;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -12,29 +12,43 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
public final class PromiseTests { public final class PromiseTests {
private final Logger logger = LoggerFactory.getLogger(PromiseTests.class); private final Logger logger = LoggerFactory.getLogger(PromiseTests.class);
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(5); private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(6);
private final PromiseFactory pfac = new SimplePromiseFactory<>(new SinglePoolExecutor(executor), logger); private final PromiseFactory promises = PromiseFactory.of(logger, executor);
@Test
public void testErrors() {
Promise<?> promise = promises.start().thenSupplyAsync(() -> {
throw new StackOverflowError();
});
try {
promise.await();
} catch (CompletionException e) {
assert e.getCause() instanceof StackOverflowError;
}
}
@Test @Test
public void testShutdown() { public void testShutdown() {
executor.shutdown(); executor.close();
Promise<?> promise = pfac.resolve(null).thenSupplyAsync(() -> null); Promise<?> promise = promises.resolve(null).thenSupplyAsync(() -> null);
try { try {
promise.await(); promise.await();
} catch (RuntimeException e) { } catch (CompletionException e) {
assert e.getCause() instanceof RejectedExecutionException; assert e.getCause() instanceof RejectedExecutionException;
} }
} }
@Test @Test
public void testErrorCancellation() throws InterruptedException { public void testCancellation() throws InterruptedException {
var finished = new AtomicBoolean(); var finished = new AtomicBoolean();
pfac.start() promises.start().thenRunDelayedAsync(() -> finished.set(true), 50, TimeUnit.MILLISECONDS)
.thenRunDelayedAsync(() -> finished.set(true), 50, TimeUnit.MILLISECONDS)
.thenRunAsync(() -> {}) .thenRunAsync(() -> {})
.cancel(); .cancel();
@@ -42,13 +56,25 @@ public final class PromiseTests {
assert !finished.get(); assert !finished.get();
} }
@Test
public void testFork() throws InterruptedException {
var finished = new AtomicBoolean();
promises.start()
.thenRunDelayedAsync(() -> finished.set(true), 50, TimeUnit.MILLISECONDS)
.fork()
.cancel();
Thread.sleep(100L);
assert finished.get();
}
@Test @Test
public void testToFuture() throws InterruptedException { public void testToFuture() throws InterruptedException {
assert pfac.resolve(true).toFuture().getNow(false); assert promises.resolve(true).toFuture().getNow(false);
assert pfac.error(new Exception("Test")).toFuture().isCompletedExceptionally(); assert promises.error(new Exception("Test")).toFuture().isCompletedExceptionally();
var finished = new AtomicBoolean(); var finished = new AtomicBoolean();
pfac.start() promises.start()
.thenRunDelayedAsync(() -> finished.set(true), 50, TimeUnit.MILLISECONDS) .thenRunDelayedAsync(() -> finished.set(true), 50, TimeUnit.MILLISECONDS)
.toFuture() .toFuture()
.cancel(true); .cancel(true);
@@ -58,86 +84,81 @@ public final class PromiseTests {
} }
@Test @Test
public void testCombineUtil() throws TimeoutException { public void testCombineUtil() throws TimeoutException, ExecutionException, InterruptedException {
pfac.all( promises.all(
pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS) promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS)
) )
.join(100L); .get(100L, TimeUnit.MILLISECONDS);
pfac.allSettled( promises.allSettled(
pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS) promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS)
) )
.join(100L); .get(100L, TimeUnit.MILLISECONDS);
pfac.combine( promises.combine(
pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS) promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS)
) )
.join(100L); .get(100L, TimeUnit.MILLISECONDS);
pfac.combine( promises.combine(
List.of( List.of(
pfac.start().thenRunDelayedAsync(() -> {}, 49, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> {}, 49, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> {}, 51, TimeUnit.MILLISECONDS) promises.start().thenRunDelayedAsync(() -> {}, 51, TimeUnit.MILLISECONDS)
) )
) )
.join(100L); .get(100L, TimeUnit.MILLISECONDS);
pfac.combine( promises.combineMapped(
Map.of( Map.of(
"a", pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS), "a", promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS),
"b", pfac.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS) "b", promises.start().thenRunDelayedAsync(() -> {}, 50, TimeUnit.MILLISECONDS)
) )
) )
.join(100L); .get(100L, TimeUnit.MILLISECONDS);
} }
@Test @Test
public void testCombineUtilPropagation() throws InterruptedException { public void testCombineUtilPropagation() throws InterruptedException {
var finished1 = new AtomicBoolean(); var finished1 = new AtomicBoolean();
pfac.all( promises.all(
true, promises.start().thenRunDelayedAsync(() -> finished1.set(true), 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> finished1.set(true), 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> finished1.set(true), 50, TimeUnit.MILLISECONDS)
pfac.start().thenRunDelayedAsync(() -> finished1.set(true), 50, TimeUnit.MILLISECONDS)
) )
.cancel(); .cancel();
var finished2 = new AtomicBoolean(); var finished2 = new AtomicBoolean();
pfac.allSettled( promises.allSettled(
true, promises.start().thenRunDelayedAsync(() -> finished2.set(true), 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> finished2.set(true), 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> finished2.set(true), 50, TimeUnit.MILLISECONDS)
pfac.start().thenRunDelayedAsync(() -> finished2.set(true), 50, TimeUnit.MILLISECONDS)
) )
.cancel(); .cancel();
var finished3 = new AtomicBoolean(); var finished3 = new AtomicBoolean();
pfac.combine( promises.combine(
true, promises.start().thenRunDelayedAsync(() -> finished3.set(true), 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> finished3.set(true), 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> finished3.set(true), 50, TimeUnit.MILLISECONDS)
pfac.start().thenRunDelayedAsync(() -> finished3.set(true), 50, TimeUnit.MILLISECONDS)
) )
.cancel(); .cancel();
var finished4 = new AtomicBoolean(); var finished4 = new AtomicBoolean();
pfac.combine( promises.combine(
true,
List.of( List.of(
pfac.start().thenRunDelayedAsync(() -> finished4.set(true), 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> finished4.set(true), 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> finished4.set(true), 50, TimeUnit.MILLISECONDS), promises.start().thenRunDelayedAsync(() -> finished4.set(true), 50, TimeUnit.MILLISECONDS),
pfac.start().thenRunDelayedAsync(() -> finished4.set(true), 50, TimeUnit.MILLISECONDS) promises.start().thenRunDelayedAsync(() -> finished4.set(true), 50, TimeUnit.MILLISECONDS)
) )
) )
.cancel(); .cancel();
var finished5 = new AtomicBoolean(); var finished5 = new AtomicBoolean();
pfac.combine( promises.combineMapped(
true,
Map.of( Map.of(
"a", pfac.start().thenRunDelayedAsync(() -> finished5.set(true), 50, TimeUnit.MILLISECONDS), "a", promises.start().thenRunDelayedAsync(() -> finished5.set(true), 50, TimeUnit.MILLISECONDS),
"b", pfac.start().thenRunDelayedAsync(() -> finished5.set(true), 50, TimeUnit.MILLISECONDS) "b", promises.start().thenRunDelayedAsync(() -> finished5.set(true), 50, TimeUnit.MILLISECONDS)
) )
) )
.cancel(); .cancel();
@@ -151,13 +172,87 @@ public final class PromiseTests {
} }
@Test @Test
public void testRace() throws TimeoutException { public void testRace() {
assert pfac.race( assert promises.race(
List.of( List.of(
pfac.start().thenSupplyDelayedAsync(() -> true, 150, TimeUnit.MILLISECONDS), promises.start().thenSupplyDelayedAsync(() -> true, 150, TimeUnit.MILLISECONDS),
pfac.start().thenSupplyDelayedAsync(() -> false, 200, TimeUnit.MILLISECONDS) promises.start().thenSupplyDelayedAsync(() -> false, 200, TimeUnit.MILLISECONDS)
) )
).join(300L); ).await();
}
@Test
public void testOrDefault() {
CompletablePromise<Integer> promise = promises.unresolved();
AtomicReference<Integer> res = new AtomicReference<>();
promise.orDefault(10).thenPopulateReference(res);
promise.completeExceptionally(new IllegalStateException("Test"));
assert res.get() == 10;
}
@Test
public void testOrDefaultSupplier() {
CompletablePromise<Integer> promise = promises.unresolved();
AtomicReference<Integer> res = new AtomicReference<>();
promise.orDefault(() -> 10).thenPopulateReference(res);
promise.completeExceptionally(new IllegalStateException("Test"));
assert res.get() == 10;
}
@Test
public void testOrDefaultFunction() {
CompletablePromise<Integer> promise = promises.unresolved();
AtomicReference<Integer> res = new AtomicReference<>();
promise.orDefault(e -> {
assert e instanceof IllegalStateException;
return 10;
}).thenPopulateReference(res);
promise.completeExceptionally(new IllegalStateException("Test"));
assert res.get() == 10;
}
@Test
public void testOrDefaultError() {
CompletablePromise<Integer> promise = promises.unresolved();
AtomicReference<Integer> res = new AtomicReference<>();
Promise<Integer> promise2 = promise.orDefault(e -> {
throw new IllegalStateException("Test");
}).thenPopulateReference(res);
promise.completeExceptionally(new IllegalStateException("Test"));
assert res.get() == null;
assert promise2.getCompletion() != null && promise2.getCompletion().getException() instanceof IllegalStateException;
}
@Test
public void testStream() {
var res = promises.combine(Stream.of(1, 2, 3).map(promises::resolve)).await();
assert res.size() == 3;
}
@Test
public void combineMappedTest() {
var res = promises.combineMapped(List.of(1, 2, 3),
n -> promises.start().thenSupplyDelayedAsync(() -> n * 2, 50, TimeUnit.MILLISECONDS)
).await();
assert res.size() == 3;
assert res.get(1) == 2;
assert res.get(2) == 4;
assert res.get(3) == 6;
}
@Test
public void testImmediate1() {
var promise = promises.start().thenSupply(() -> 10);
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<?,?,?>;
} }
} }

3
futur-lazy/build.gradle Normal file
View File

@@ -0,0 +1,3 @@
dependencies {
api project(':futur-api')
}

Binary file not shown.

View File

@@ -0,0 +1,33 @@
const PACKAGE = "src/main/java/dev/tommyjs/futur"
const SIGNAL = "// Generated delegates to static factory"
const regex = /( {4}\/\*\*.+?)?((?:\S| )+? )(\S+)(\(.*?\))(?: {.+?}|;)/gs
const content = await Bun.file(`../../futur-api/${PACKAGE}/promise/PromiseFactory.java`).text()
const methods = [""]
for (const match of content.matchAll(regex)) {
let [_, docs, head, name, params] = match
head = head.trimStart()
if (head.startsWith("static")) continue
if (head.startsWith("default")) head = head.slice(8);
const args = Array.from(params.matchAll(/ ([a-zA-Z1-9]+)[,)]/gs))
.map(v => v[1]).join(", ")
methods.push(
[
`${docs} public static ${head}${name}${params} {`,
` return factory.${name}(${args});`,
" }"
].join("\n")
)
}
const output = Bun.file(`../${PACKAGE}/lazy/Promises.java`)
const existing = await output.text()
const cutIndex = existing.indexOf(SIGNAL) + SIGNAL.length;
const newContent = existing.slice(0, cutIndex) + methods.join("\n\n") + "\n\n}"
await Bun.write(output, newContent)

View File

@@ -0,0 +1,11 @@
{
"name": "generate-promises",
"module": "index.ts",
"type": "module",
"devDependencies": {
"@types/bun": "latest"
},
"peerDependencies": {
"typescript": "latest"
}
}

View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
// Enable latest features
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}

View File

@@ -0,0 +1,498 @@
package dev.tommyjs.futur.lazy;
import dev.tommyjs.futur.executor.PromiseExecutor;
import dev.tommyjs.futur.promise.CompletablePromise;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Spliterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Stream;
@SuppressWarnings("unchecked")
public final class Promises {
private static final Logger LOGGER = LoggerFactory.getLogger(Promises.class);
private static PromiseFactory factory = PromiseFactory.of(LOGGER, PromiseExecutor.virtualThreaded());
public static void useFactory(@NotNull PromiseFactory factory) {
Promises.factory = factory;
}
// Generated delegates to static factory
/**
* Creates a new uncompleted promise.
*
* @return the new promise
*/
public static <T> @NotNull CompletablePromise<T> unresolved() {
return factory.unresolved();
}
/**
* Creates a new promise, completed with the given value.
*
* @param value the value to complete the promise with
* @return the new promise
*/
public static <T> @NotNull Promise<T> resolve(T value) {
return factory.resolve(value);
}
/**
* Creates a new promise, completed with {@code null}. This method is often useful for starting
* promise chains.
*
* @return the new promise
*/
public static @NotNull Promise<Void> start() {
return factory.start();
}
/**
* Creates a new promise, completed exceptionally with the given error.
*
* @param error the error to complete the promise with
* @return the new promise
*/
public static <T> @NotNull Promise<T> error(@NotNull Throwable error) {
return factory.error(error);
}
/**
* Creates a new promise backed by the given completion and future.
* The promise will be completed upon completion of the {@link CompletionStage}
* and the {@link Future} will be cancelled upon cancellation of the promise.
*
* @param completion the completion stage to wrap
* @param future the future to wrap
* @return the new promise
*/
public static <T> @NotNull Promise<T> wrap(@NotNull CompletionStage<T> completion, @Nullable Future<T> future) {
return factory.wrap(completion, future);
}
/**
* Creates a new promise backed by the given future.
* The promise will be completed upon completion of the {@link CompletableFuture}
* and the {@link CompletableFuture} will be cancelled upon cancellation of the promise.
*
* @param future the future to wrap
* @return the new promise
*/
public static <T> @NotNull Promise<T> wrap(@NotNull CompletableFuture<T> future) {
return factory.wrap(future);
}
/**
* Combines two promises into a single promise that resolves when both promises are completed.
* If either input promise completes exceptionally, the other promise will be cancelled
* and the output promise will complete exceptionally.
*
* @param p1 the first promise
* @param p2 the second promise
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map.Entry<K, V>> combine(@NotNull Promise<K> p1, @NotNull Promise<V> p2) {
return factory.combine(p1, p2);
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @param expectedSize the expected size of the iterator (used for optimization)
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterator<Map.Entry<K, Promise<V>>> promises,
int expectedSize) {
return factory.combineMapped(promises, expectedSize);
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Spliterator<Map.Entry<K, Promise<V>>> promises) {
return factory.combineMapped(promises);
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Stream<Map.Entry<K, Promise<V>>> promises) {
return factory.combineMapped(promises);
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterable<Map.Entry<K, Promise<V>>> promises) {
return factory.combineMapped(promises);
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Map.Entry<K, Promise<V>>... promises) {
return factory.combineMapped(promises);
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Map<K, Promise<V>> promises) {
return factory.combineMapped(promises);
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param keys the keys to map to promises
* @param mapper the function to map keys to promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Stream<K> keys,
@NotNull Function<K, Promise<V>> mapper) {
return factory.combineMapped(keys, mapper);
}
/**
* Combines key-value pairs of promises into a single promise that completes
* when all promises are completed, with the results mapped by their keys.
* If any promise completes exceptionally, the other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param keys the keys to map to promises
* @param mapper the function to map keys to promises
* @return the combined promise
*/
public static <K, V> @NotNull Promise<Map<K, V>> combineMapped(@NotNull Iterable<K> keys,
@NotNull Function<K, Promise<V>> mapper) {
return factory.combineMapped(keys, mapper);
}
/**
* @deprecated Use combineMapped instead.
*/
@Deprecated
public static <K, V> @NotNull Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises) {
return factory.combine(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @param expectedSize the expected size of the iterator (used for optimization)
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Iterator<Promise<V>> promises, int expectedSize) {
return factory.combine(promises, expectedSize);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Spliterator<Promise<V>> promises) {
return factory.combine(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Stream<Promise<V>> promises) {
return factory.combine(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Iterable<Promise<V>> promises) {
return factory.combine(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of results in the original order.
* If any promise completes exceptionally, all other promises will be cancelled
* and the combined promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static <V> @NotNull Promise<List<V>> combine(@NotNull Promise<V>... promises) {
return factory.combine(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @param expectedSize the expected size of the iterator (used for optimization)
* @return the combined promise
*/
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Iterator<Promise<?>> promises,
int expectedSize) {
return factory.allSettled(promises, expectedSize);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @return the combined promise
*/
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Spliterator<Promise<?>> promises) {
return factory.allSettled(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @return the combined promise
*/
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Stream<Promise<?>> promises) {
return factory.allSettled(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @return the combined promise
*/
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Iterable<Promise<?>> promises) {
return factory.allSettled(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises
* are completed, with a list of completions in the original order.
*
* @param promises the input promises
* @return the combined promise
*/
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Promise<?>... promises) {
return factory.allSettled(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises complete.
* If any promise completes exceptionally, all other promises will be cancelled
* and the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static @NotNull Promise<Void> all(@NotNull Iterator<Promise<?>> promises) {
return factory.all(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises complete.
* If any promise completes exceptionally, all other promises will be cancelled
* and the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static @NotNull Promise<Void> all(@NotNull Stream<Promise<?>> promises) {
return factory.all(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises complete.
* If any promise completes exceptionally, all other promises will be cancelled
* and the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static @NotNull Promise<Void> all(@NotNull Iterable<Promise<?>> promises) {
return factory.all(promises);
}
/**
* Combines multiple promises into a single promise that completes when all promises complete.
* If any promise completes exceptionally, all other promises will be cancelled
* and the output promise will complete exceptionally.
*
* @param promises the input promises
* @return the combined promise
*/
public static @NotNull Promise<Void> all(@NotNull Promise<?>... promises) {
return factory.all(promises);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If {@code ignoreErrors} is {@code false} and the first promise completed exceptionally, the
* combined promise will also complete exceptionally. Otherwise, the combined promise will wait for a
* successful completion or complete with {@code null} if all promises complete exceptionally.
* Additionally, if {@code cancelLosers} is {@code true}, the other promises will be cancelled
* once the combined promise is completed.
*
* @param promises the input promises
* @param ignoreErrors whether to ignore promises that complete exceptionally
* @return the combined promise
*/
public static <V> @NotNull Promise<V> race(@NotNull Iterator<Promise<V>> promises, boolean ignoreErrors) {
return factory.race(promises, ignoreErrors);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If {@code ignoreErrors} is {@code false} and the first promise completed exceptionally, the
* combined promise will also complete exceptionally. Otherwise, the combined promise will wait for a
* successful completion or complete with {@code null} if all promises complete exceptionally.
* Additionally, The other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @param ignoreErrors whether to ignore promises that complete exceptionally
* @return the combined promise
*/
public static <V> @NotNull Promise<V> race(@NotNull Stream<Promise<V>> promises, boolean ignoreErrors) {
return factory.race(promises, ignoreErrors);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If the first promise completed exceptionally, the combined promise will also complete exceptionally.
* Additionally, the other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @return the combined promise
*/
public static <V> @NotNull Promise<V> race(@NotNull Stream<Promise<V>> promises) {
return factory.race(promises);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If {@code ignoreErrors} is {@code false} and the first promise completed exceptionally, the
* combined promise will also complete exceptionally. Otherwise, the combined promise will wait for a
* successful completion or complete with {@code null} if all promises complete exceptionally.
* Additionally, The other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @param ignoreErrors whether to ignore promises that complete exceptionally
* @return the combined promise
*/
public static <V> @NotNull Promise<V> race(@NotNull Iterable<Promise<V>> promises, boolean ignoreErrors) {
return factory.race(promises, ignoreErrors);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If the first promise completed exceptionally, the combined promise will also complete exceptionally.
* Additionally, the other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @return the combined promise
*/
public static <V> @NotNull Promise<V> race(@NotNull Iterable<Promise<V>> promises) {
return factory.race(promises);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If {@code ignoreErrors} is {@code false} and the first promise completed exceptionally, the
* combined promise will also complete exceptionally. Otherwise, the combined promise will wait for a
* successful completion or complete with {@code null} if all promises complete exceptionally.
* Additionally, The other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @param ignoreErrors whether to ignore promises that complete exceptionally
* @return the combined promise
*/
public static <V> @NotNull Promise<V> race(boolean ignoreErrors, @NotNull Promise<V>... promises) {
return factory.race(ignoreErrors, promises);
}
/**
* Combines multiple promises into a single promise that completes when any promise is completed.
* If the first promise completed exceptionally, the combined promise will also complete exceptionally.
* Additionally, the other promises will be cancelled once the combined promise is completed.
*
* @param promises the input promises
* @return the combined promise
*/
public static <V> @NotNull Promise<V> race(@NotNull Promise<V>... promises) {
return factory.race(promises);
}
}

View File

@@ -1,6 +0,0 @@
apply plugin: 'java-library'
dependencies {
api project(':futur-api')
testImplementation project(':futur-api')
}

View File

@@ -1,127 +0,0 @@
package dev.tommyjs.futur.lazy;
import dev.tommyjs.futur.promise.Promise;
import dev.tommyjs.futur.promise.PromiseCompletion;
import dev.tommyjs.futur.promise.PromiseFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
public final class PromiseUtil {
private static PromiseFactory pfac = StaticPromiseFactory.INSTANCE;
public static @NotNull Logger getLogger() {
return pfac.getLogger();
}
public static void setPromiseFactory(PromiseFactory pfac) {
PromiseUtil.pfac = pfac;
}
public static @NotNull <T> Promise<T> unresolved() {
return pfac.unresolved();
}
public static @NotNull <K, V> Promise<Map.Entry<K, V>> combine(boolean propagateCancel, @NotNull Promise<K> p1, @NotNull Promise<V> p2) {
return pfac.combine(propagateCancel, p1, p2);
}
public static @NotNull <K, V> Promise<Map.Entry<K, V>> combine(@NotNull Promise<K> p1, @NotNull Promise<V> p2) {
return pfac.combine(p1, p2);
}
public static @NotNull <K, V> Promise<Map<K, V>> combine(boolean propagateCancel, @NotNull Map<K, Promise<V>> promises, @Nullable BiConsumer<K, Throwable> exceptionHandler) {
return pfac.combine(propagateCancel, promises, exceptionHandler);
}
public static @NotNull <K, V> Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises, @Nullable BiConsumer<K, Throwable> exceptionHandler) {
return pfac.combine(promises, exceptionHandler);
}
public static @NotNull <K, V> Promise<Map<K, V>> combine(boolean propagateCancel, @NotNull Map<K, Promise<V>> promises) {
return pfac.combine(propagateCancel, promises);
}
public static @NotNull <K, V> Promise<Map<K, V>> combine(@NotNull Map<K, Promise<V>> promises) {
return pfac.combine(promises);
}
public static @NotNull <V> Promise<List<V>> combine(boolean propagateCancel, @NotNull Iterable<Promise<V>> promises, @Nullable BiConsumer<Integer, Throwable> exceptionHandler) {
return pfac.combine(propagateCancel, promises, exceptionHandler);
}
public static @NotNull <V> Promise<List<V>> combine(@NotNull Iterable<Promise<V>> promises, @Nullable BiConsumer<Integer, Throwable> exceptionHandler) {
return pfac.combine(promises, exceptionHandler);
}
public static @NotNull <V> Promise<List<V>> combine(boolean propagateCancel, @NotNull Iterable<Promise<V>> promises) {
return pfac.combine(propagateCancel, promises);
}
public static @NotNull <V> Promise<List<V>> combine(@NotNull Iterable<Promise<V>> promises) {
return pfac.combine(promises);
}
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(boolean propagateCancel, @NotNull Iterable<Promise<?>> promiseIterable) {
return pfac.allSettled(propagateCancel, promiseIterable);
}
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Iterable<Promise<?>> promiseIterable) {
return pfac.allSettled(promiseIterable);
}
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(boolean propagateCancel, @NotNull Promise<?>... promiseArray) {
return pfac.allSettled(propagateCancel, promiseArray);
}
public static @NotNull Promise<List<PromiseCompletion<?>>> allSettled(@NotNull Promise<?>... promiseArray) {
return pfac.allSettled(promiseArray);
}
public static @NotNull Promise<Void> all(boolean propagateCancel, @NotNull Iterable<Promise<?>> promiseIterable) {
return pfac.all(propagateCancel, promiseIterable);
}
public static @NotNull Promise<Void> all(@NotNull Iterable<Promise<?>> promiseIterable) {
return pfac.all(promiseIterable);
}
public static @NotNull Promise<Void> all(boolean propagateCancel, @NotNull Promise<?>... promiseArray) {
return pfac.all(propagateCancel, promiseArray);
}
public static @NotNull Promise<Void> all(@NotNull Promise<?>... promiseArray) {
return pfac.all(promiseArray);
}
public static <V> @NotNull Promise<V> race(@NotNull Iterable<Promise<V>> promises) {
return pfac.race(promises);
}
public static <V> @NotNull Promise<V> race(boolean cancelRaceLosers, @NotNull Iterable<Promise<V>> promises) {
return pfac.race(cancelRaceLosers, promises);
}
public static @NotNull <T> Promise<T> wrap(@NotNull CompletableFuture<T> future) {
return pfac.wrap(future);
}
public static @NotNull <T> Promise<T> resolve(T value) {
return pfac.resolve(value);
}
public static @NotNull Promise<Void> start() {
return pfac.start();
}
public static @NotNull <T> Promise<T> error(@NotNull Throwable error) {
return pfac.error(error);
}
}

View File

@@ -1,38 +0,0 @@
package dev.tommyjs.futur.lazy;
import dev.tommyjs.futur.executor.PromiseExecutor;
import dev.tommyjs.futur.executor.SinglePoolExecutor;
import dev.tommyjs.futur.impl.SimplePromise;
import dev.tommyjs.futur.promise.AbstractPromiseFactory;
import dev.tommyjs.futur.promise.Promise;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Future;
public final class StaticPromiseFactory extends AbstractPromiseFactory<Future<?>> {
public final static StaticPromiseFactory INSTANCE = new StaticPromiseFactory();
private final static @NotNull SinglePoolExecutor EXECUTOR = SinglePoolExecutor.create(1);
private final static @NotNull Logger LOGGER = LoggerFactory.getLogger(StaticPromiseFactory.class);
private StaticPromiseFactory() {
}
@Override
public @NotNull Logger getLogger() {
return LOGGER;
}
@Override
public @NotNull <T> Promise<T> unresolved() {
return new SimplePromise<>(this);
}
@Override
public @NotNull PromiseExecutor<Future<?>> getExecutor() {
return EXECUTOR;
}
}

Binary file not shown.

View File

@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

33
gradlew vendored
View File

@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -83,10 +85,8 @@ done
# This is normally unused # This is normally unused
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -133,18 +133,21 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045 # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
@@ -152,7 +155,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045 # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@@ -197,11 +200,15 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
# shell script including quotes and variable substitutions, so put them in DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded. # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \

22
gradlew.bat vendored
View File

@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail

View File

@@ -1,4 +1,4 @@
rootProject.name = 'futur' rootProject.name = 'futur'
include 'futur-api' include 'futur-api'
include 'futur-static' include 'futur-lazy'