8152924: Improve scalability of CompletableFuture with large number of dependents
Reviewed-by: martin, psandoz
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Thu Apr 07 13:41:46 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Thu Apr 07 09:57:08 2016 -0700
@@ -583,9 +583,9 @@
*/
final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) {
if (a != null && a.stack != null) {
- if (mode < 0 || a.result == null)
+ if (a.result == null)
a.cleanStack();
- else
+ else if (mode >= 0)
a.postComplete();
}
if (result != null && stack != null) {
@@ -1107,9 +1107,9 @@
final CompletableFuture<T> postFire(CompletableFuture<?> a,
CompletableFuture<?> b, int mode) {
if (b != null && b.stack != null) { // clean second source
- if (mode < 0 || b.result == null)
+ if (b.result == null)
b.cleanStack();
- else
+ else if (mode >= 0)
b.postComplete();
}
return postFire(a, mode);
--- a/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java Thu Apr 07 13:41:46 2016 +0200
+++ b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java Thu Apr 07 09:57:08 2016 -0700
@@ -3952,6 +3952,38 @@
Monad.plus(godot, Monad.unit(5L)));
}
+ /**
+ * A single CompletableFuture with many dependents.
+ * A demo of scalability - runtime is O(n).
+ */
+ public void testManyDependents() throws Throwable {
+ final int n = 1_000;
+ final CompletableFuture<Void> head = new CompletableFuture<>();
+ final CompletableFuture<Void> complete = CompletableFuture.completedFuture((Void)null);
+ final AtomicInteger count = new AtomicInteger(0);
+ for (int i = 0; i < n; i++) {
+ head.thenRun(() -> count.getAndIncrement());
+ head.thenAccept((x) -> count.getAndIncrement());
+ head.thenApply((x) -> count.getAndIncrement());
+
+ head.runAfterBoth(complete, () -> count.getAndIncrement());
+ head.thenAcceptBoth(complete, (x, y) -> count.getAndIncrement());
+ head.thenCombine(complete, (x, y) -> count.getAndIncrement());
+ complete.runAfterBoth(head, () -> count.getAndIncrement());
+ complete.thenAcceptBoth(head, (x, y) -> count.getAndIncrement());
+ complete.thenCombine(head, (x, y) -> count.getAndIncrement());
+
+ head.runAfterEither(new CompletableFuture<Void>(), () -> count.getAndIncrement());
+ head.acceptEither(new CompletableFuture<Void>(), (x) -> count.getAndIncrement());
+ head.applyToEither(new CompletableFuture<Void>(), (x) -> count.getAndIncrement());
+ new CompletableFuture<Void>().runAfterEither(head, () -> count.getAndIncrement());
+ new CompletableFuture<Void>().acceptEither(head, (x) -> count.getAndIncrement());
+ new CompletableFuture<Void>().applyToEither(head, (x) -> count.getAndIncrement());
+ }
+ head.complete(null);
+ assertEquals(5 * 3 * n, count.get());
+ }
+
// static <U> U join(CompletionStage<U> stage) {
// CompletableFuture<U> f = new CompletableFuture<>();
// stage.whenComplete((v, ex) -> {