--- jsr166/src/test/tck/CompletableFutureTest.java 2016/07/03 19:38:19 1.166 +++ jsr166/src/test/tck/CompletableFutureTest.java 2017/01/04 06:09:58 1.183 @@ -32,7 +32,6 @@ import java.util.concurrent.ForkJoinPool import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeoutException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; @@ -94,10 +93,10 @@ public class CompletableFutureTest exten * Returns the "raw" internal exceptional completion of f, * without any additional wrapping with CompletionException. */ - Throwable exceptionalCompletion(CompletableFuture f) { - // handle (and whenComplete) can distinguish between "direct" - // and "wrapped" exceptional completion - return f.handle((U u, Throwable t) -> t).join(); + Throwable exceptionalCompletion(CompletableFuture f) { + // handle (and whenComplete and exceptionally) can distinguish + // between "direct" and "wrapped" exceptional completion + return f.handle((u, t) -> t).join(); } void checkCompletedExceptionally(CompletableFuture f, @@ -148,26 +147,26 @@ public class CompletableFutureTest exten void checkCompletedWithWrappedCFException(CompletableFuture f) { checkCompletedExceptionally(f, true, - (t) -> assertTrue(t instanceof CFException)); + t -> assertTrue(t instanceof CFException)); } void checkCompletedWithWrappedCancellationException(CompletableFuture f) { checkCompletedExceptionally(f, true, - (t) -> assertTrue(t instanceof CancellationException)); + t -> assertTrue(t instanceof CancellationException)); } void checkCompletedWithTimeoutException(CompletableFuture f) { checkCompletedExceptionally(f, false, - (t) -> assertTrue(t instanceof TimeoutException)); + t -> assertTrue(t instanceof TimeoutException)); } void checkCompletedWithWrappedException(CompletableFuture f, Throwable ex) { - checkCompletedExceptionally(f, true, (t) -> assertSame(t, ex)); + checkCompletedExceptionally(f, true, t -> assertSame(t, ex)); } void checkCompletedExceptionally(CompletableFuture f, Throwable ex) { - checkCompletedExceptionally(f, false, (t) -> assertSame(t, ex)); + checkCompletedExceptionally(f, false, t -> assertSame(t, ex)); } void checkCancelled(CompletableFuture f) { @@ -362,7 +361,7 @@ public class CompletableFutureTest exten checkCompletedNormally(f, "test"); } - abstract class CheckedAction { + abstract static class CheckedAction { int invocationCount = 0; final ExecutionMode m; CheckedAction(ExecutionMode m) { this.m = m; } @@ -374,7 +373,7 @@ public class CompletableFutureTest exten void assertInvoked() { assertEquals(1, invocationCount); } } - abstract class CheckedIntegerAction extends CheckedAction { + abstract static class CheckedIntegerAction extends CheckedAction { Integer value; CheckedIntegerAction(ExecutionMode m) { super(m); } void assertValue(Integer expected) { @@ -383,7 +382,7 @@ public class CompletableFutureTest exten } } - class IntegerSupplier extends CheckedAction + static class IntegerSupplier extends CheckedAction implements Supplier { final Integer value; @@ -402,7 +401,7 @@ public class CompletableFutureTest exten return (x == null) ? null : x + 1; } - class NoopConsumer extends CheckedIntegerAction + static class NoopConsumer extends CheckedIntegerAction implements Consumer { NoopConsumer(ExecutionMode m) { super(m); } @@ -412,7 +411,7 @@ public class CompletableFutureTest exten } } - class IncFunction extends CheckedIntegerAction + static class IncFunction extends CheckedIntegerAction implements Function { IncFunction(ExecutionMode m) { super(m); } @@ -430,7 +429,7 @@ public class CompletableFutureTest exten - ((y == null) ? 99 : y.intValue()); } - class SubtractAction extends CheckedIntegerAction + static class SubtractAction extends CheckedIntegerAction implements BiConsumer { SubtractAction(ExecutionMode m) { super(m); } @@ -440,7 +439,7 @@ public class CompletableFutureTest exten } } - class SubtractFunction extends CheckedIntegerAction + static class SubtractFunction extends CheckedIntegerAction implements BiFunction { SubtractFunction(ExecutionMode m) { super(m); } @@ -450,14 +449,14 @@ public class CompletableFutureTest exten } } - class Noop extends CheckedAction implements Runnable { + static class Noop extends CheckedAction implements Runnable { Noop(ExecutionMode m) { super(m); } public void run() { invoked(); } } - class FailingSupplier extends CheckedAction + static class FailingSupplier extends CheckedAction implements Supplier { final CFException ex; @@ -468,7 +467,7 @@ public class CompletableFutureTest exten } } - class FailingConsumer extends CheckedIntegerAction + static class FailingConsumer extends CheckedIntegerAction implements Consumer { final CFException ex; @@ -480,7 +479,7 @@ public class CompletableFutureTest exten } } - class FailingBiConsumer extends CheckedIntegerAction + static class FailingBiConsumer extends CheckedIntegerAction implements BiConsumer { final CFException ex; @@ -492,7 +491,7 @@ public class CompletableFutureTest exten } } - class FailingFunction extends CheckedIntegerAction + static class FailingFunction extends CheckedIntegerAction implements Function { final CFException ex; @@ -504,7 +503,7 @@ public class CompletableFutureTest exten } } - class FailingBiFunction extends CheckedIntegerAction + static class FailingBiFunction extends CheckedIntegerAction implements BiFunction { final CFException ex; @@ -516,7 +515,7 @@ public class CompletableFutureTest exten } } - class FailingRunnable extends CheckedAction implements Runnable { + static class FailingRunnable extends CheckedAction implements Runnable { final CFException ex; FailingRunnable(ExecutionMode m) { super(m); ex = new CFException(); } public void run() { @@ -525,7 +524,7 @@ public class CompletableFutureTest exten } } - class CompletableFutureInc extends CheckedIntegerAction + static class CompletableFutureInc extends CheckedIntegerAction implements Function> { CompletableFutureInc(ExecutionMode m) { super(m); } @@ -538,7 +537,7 @@ public class CompletableFutureTest exten } } - class FailingCompletableFutureFunction extends CheckedIntegerAction + static class FailingCompletableFutureFunction extends CheckedIntegerAction implements Function> { final CFException ex; @@ -3258,12 +3257,12 @@ public class CompletableFutureTest exten () -> f.thenApply(null), () -> f.thenApplyAsync(null), - () -> f.thenApplyAsync((x) -> x, null), + () -> f.thenApplyAsync(x -> x, null), () -> f.thenApplyAsync(null, exec), () -> f.thenAccept(null), () -> f.thenAcceptAsync(null), - () -> f.thenAcceptAsync((x) -> {} , null), + () -> f.thenAcceptAsync(x -> {} , null), () -> f.thenAcceptAsync(null, exec), () -> f.thenRun(null), @@ -3298,18 +3297,18 @@ public class CompletableFutureTest exten () -> f.applyToEither(g, null), () -> f.applyToEitherAsync(g, null), () -> f.applyToEitherAsync(g, null, exec), - () -> f.applyToEither(nullFuture, (x) -> x), - () -> f.applyToEitherAsync(nullFuture, (x) -> x), - () -> f.applyToEitherAsync(nullFuture, (x) -> x, exec), - () -> f.applyToEitherAsync(g, (x) -> x, null), + () -> f.applyToEither(nullFuture, x -> x), + () -> f.applyToEitherAsync(nullFuture, x -> x), + () -> f.applyToEitherAsync(nullFuture, x -> x, exec), + () -> f.applyToEitherAsync(g, x -> x, null), () -> f.acceptEither(g, null), () -> f.acceptEitherAsync(g, null), () -> f.acceptEitherAsync(g, null, exec), - () -> f.acceptEither(nullFuture, (x) -> {}), - () -> f.acceptEitherAsync(nullFuture, (x) -> {}), - () -> f.acceptEitherAsync(nullFuture, (x) -> {}, exec), - () -> f.acceptEitherAsync(g, (x) -> {}, null), + () -> f.acceptEither(nullFuture, x -> {}), + () -> f.acceptEitherAsync(nullFuture, x -> {}), + () -> f.acceptEitherAsync(nullFuture, x -> {}, exec), + () -> f.acceptEitherAsync(g, x -> {}, null), () -> f.runAfterEither(g, null), () -> f.runAfterEitherAsync(g, null), @@ -3375,18 +3374,18 @@ public class CompletableFutureTest exten for (CompletableFuture src : srcs) { List> fs = new ArrayList<>(); fs.add(src.thenRunAsync(() -> {}, e)); - fs.add(src.thenAcceptAsync((z) -> {}, e)); - fs.add(src.thenApplyAsync((z) -> z, e)); + fs.add(src.thenAcceptAsync(z -> {}, e)); + fs.add(src.thenApplyAsync(z -> z, e)); fs.add(src.thenCombineAsync(src, (x, y) -> x, e)); fs.add(src.thenAcceptBothAsync(src, (x, y) -> {}, e)); fs.add(src.runAfterBothAsync(src, () -> {}, e)); - fs.add(src.applyToEitherAsync(src, (z) -> z, e)); - fs.add(src.acceptEitherAsync(src, (z) -> {}, e)); + fs.add(src.applyToEitherAsync(src, z -> z, e)); + fs.add(src.acceptEitherAsync(src, z -> {}, e)); fs.add(src.runAfterEitherAsync(src, () -> {}, e)); - fs.add(src.thenComposeAsync((z) -> null, e)); + fs.add(src.thenComposeAsync(z -> null, e)); fs.add(src.whenCompleteAsync((z, t) -> {}, e)); fs.add(src.handleAsync((z, t) -> null, e)); @@ -3419,11 +3418,11 @@ public class CompletableFutureTest exten { List> fs = new ArrayList<>(); - fs.add(complete.applyToEitherAsync(incomplete, (z) -> z, e)); - fs.add(incomplete.applyToEitherAsync(complete, (z) -> z, e)); + fs.add(complete.applyToEitherAsync(incomplete, z -> z, e)); + fs.add(incomplete.applyToEitherAsync(complete, z -> z, e)); - fs.add(complete.acceptEitherAsync(incomplete, (z) -> {}, e)); - fs.add(incomplete.acceptEitherAsync(complete, (z) -> {}, e)); + fs.add(complete.acceptEitherAsync(incomplete, z -> {}, e)); + fs.add(incomplete.acceptEitherAsync(complete, z -> {}, e)); fs.add(complete.runAfterEitherAsync(incomplete, () -> {}, e)); fs.add(incomplete.runAfterEitherAsync(complete, () -> {}, e)); @@ -3462,18 +3461,18 @@ public class CompletableFutureTest exten List> fs = new ArrayList<>(); fs.add(incomplete.thenRunAsync(() -> {}, e)); - fs.add(incomplete.thenAcceptAsync((z) -> {}, e)); - fs.add(incomplete.thenApplyAsync((z) -> z, e)); + fs.add(incomplete.thenAcceptAsync(z -> {}, e)); + fs.add(incomplete.thenApplyAsync(z -> z, e)); fs.add(incomplete.thenCombineAsync(incomplete, (x, y) -> x, e)); fs.add(incomplete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e)); fs.add(incomplete.runAfterBothAsync(incomplete, () -> {}, e)); - fs.add(incomplete.applyToEitherAsync(incomplete, (z) -> z, e)); - fs.add(incomplete.acceptEitherAsync(incomplete, (z) -> {}, e)); + fs.add(incomplete.applyToEitherAsync(incomplete, z -> z, e)); + fs.add(incomplete.acceptEitherAsync(incomplete, z -> {}, e)); fs.add(incomplete.runAfterEitherAsync(incomplete, () -> {}, e)); - fs.add(incomplete.thenComposeAsync((z) -> null, e)); + fs.add(incomplete.thenComposeAsync(z -> null, e)); fs.add(incomplete.whenCompleteAsync((z, t) -> {}, e)); fs.add(incomplete.handleAsync((z, t) -> null, e)); @@ -3533,7 +3532,7 @@ public class CompletableFutureTest exten */ public void testCompletedStage() { AtomicInteger x = new AtomicInteger(0); - AtomicReference r = new AtomicReference(); + AtomicReference r = new AtomicReference<>(); CompletionStage f = CompletableFuture.completedStage(1); f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); assertEquals(x.get(), 1); @@ -3578,29 +3577,53 @@ public class CompletableFutureTest exten * copy returns a CompletableFuture that is completed normally, * with the same value, when source is. */ - public void testCopy() { + public void testCopy_normalCompletion() { + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) assertTrue(f.complete(v1)); CompletableFuture g = f.copy(); - checkIncomplete(f); - checkIncomplete(g); - f.complete(1); - checkCompletedNormally(f, 1); - checkCompletedNormally(g, 1); - } + if (createIncomplete) { + checkIncomplete(f); + checkIncomplete(g); + assertTrue(f.complete(v1)); + } + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v1); + }} /** * copy returns a CompletableFuture that is completed exceptionally * when source is. */ - public void testCopy2() { + public void testCopy_exceptionalCompletion() { + for (boolean createIncomplete : new boolean[] { true, false }) + { + CFException ex = new CFException(); CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) f.completeExceptionally(ex); CompletableFuture g = f.copy(); - checkIncomplete(f); - checkIncomplete(g); - CFException ex = new CFException(); - f.completeExceptionally(ex); + if (createIncomplete) { + checkIncomplete(f); + checkIncomplete(g); + f.completeExceptionally(ex); + } checkCompletedExceptionally(f, ex); checkCompletedWithWrappedException(g, ex); + }} + + /** + * Completion of a copy does not complete its source. + */ + public void testCopy_oneWayPropagation() { + CompletableFuture f = new CompletableFuture<>(); + assertTrue(f.copy().complete(1)); + assertTrue(f.copy().complete(null)); + assertTrue(f.copy().cancel(true)); + assertTrue(f.copy().cancel(false)); + assertTrue(f.copy().completeExceptionally(new CFException())); + checkIncomplete(f); } /** @@ -3611,7 +3634,7 @@ public class CompletableFutureTest exten CompletableFuture f = new CompletableFuture<>(); CompletionStage g = f.minimalCompletionStage(); AtomicInteger x = new AtomicInteger(0); - AtomicReference r = new AtomicReference(); + AtomicReference r = new AtomicReference<>(); checkIncomplete(f); g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); f.complete(1); @@ -3628,7 +3651,7 @@ public class CompletableFutureTest exten CompletableFuture f = new CompletableFuture<>(); CompletionStage g = f.minimalCompletionStage(); AtomicInteger x = new AtomicInteger(0); - AtomicReference r = new AtomicReference(); + AtomicReference r = new AtomicReference<>(); g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); checkIncomplete(f); CFException ex = new CFException(); @@ -3646,7 +3669,7 @@ public class CompletableFutureTest exten CFException ex = new CFException(); CompletionStage f = CompletableFuture.failedStage(ex); AtomicInteger x = new AtomicInteger(0); - AtomicReference r = new AtomicReference(); + AtomicReference r = new AtomicReference<>(); f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); assertEquals(x.get(), 0); assertEquals(r.get(), ex); @@ -3670,7 +3693,7 @@ public class CompletableFutureTest exten public void testCompleteAsync2() { CompletableFuture f = new CompletableFuture<>(); CFException ex = new CFException(); - f.completeAsync(() -> {if (true) throw ex; return 1;}); + f.completeAsync(() -> { throw ex; }); try { f.join(); shouldThrow(); @@ -3700,7 +3723,7 @@ public class CompletableFutureTest exten CompletableFuture f = new CompletableFuture<>(); CFException ex = new CFException(); ThreadExecutor executor = new ThreadExecutor(); - f.completeAsync(() -> {if (true) throw ex; return 1;}, executor); + f.completeAsync(() -> { throw ex; }, executor); try { f.join(); shouldThrow(); @@ -3859,31 +3882,31 @@ public class CompletableFutureTest exten List, CompletableFuture>> funs = new ArrayList<>(); - funs.add((y) -> m.thenRun(y, noopRunnable)); - funs.add((y) -> m.thenAccept(y, noopConsumer)); - funs.add((y) -> m.thenApply(y, incFunction)); - - funs.add((y) -> m.runAfterEither(y, incomplete, noopRunnable)); - funs.add((y) -> m.acceptEither(y, incomplete, noopConsumer)); - funs.add((y) -> m.applyToEither(y, incomplete, incFunction)); - - funs.add((y) -> m.runAfterBoth(y, v42, noopRunnable)); - funs.add((y) -> m.runAfterBoth(v42, y, noopRunnable)); - funs.add((y) -> m.thenAcceptBoth(y, v42, new SubtractAction(m))); - funs.add((y) -> m.thenAcceptBoth(v42, y, new SubtractAction(m))); - funs.add((y) -> m.thenCombine(y, v42, new SubtractFunction(m))); - funs.add((y) -> m.thenCombine(v42, y, new SubtractFunction(m))); - - funs.add((y) -> m.whenComplete(y, (Integer r, Throwable t) -> {})); - - funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m))); - - funs.add((y) -> CompletableFuture.allOf(y)); - funs.add((y) -> CompletableFuture.allOf(y, v42)); - funs.add((y) -> CompletableFuture.allOf(v42, y)); - funs.add((y) -> CompletableFuture.anyOf(y)); - funs.add((y) -> CompletableFuture.anyOf(y, incomplete)); - funs.add((y) -> CompletableFuture.anyOf(incomplete, y)); + funs.add(y -> m.thenRun(y, noopRunnable)); + funs.add(y -> m.thenAccept(y, noopConsumer)); + funs.add(y -> m.thenApply(y, incFunction)); + + funs.add(y -> m.runAfterEither(y, incomplete, noopRunnable)); + funs.add(y -> m.acceptEither(y, incomplete, noopConsumer)); + funs.add(y -> m.applyToEither(y, incomplete, incFunction)); + + funs.add(y -> m.runAfterBoth(y, v42, noopRunnable)); + funs.add(y -> m.runAfterBoth(v42, y, noopRunnable)); + funs.add(y -> m.thenAcceptBoth(y, v42, new SubtractAction(m))); + funs.add(y -> m.thenAcceptBoth(v42, y, new SubtractAction(m))); + funs.add(y -> m.thenCombine(y, v42, new SubtractFunction(m))); + funs.add(y -> m.thenCombine(v42, y, new SubtractFunction(m))); + + funs.add(y -> m.whenComplete(y, (Integer r, Throwable t) -> {})); + + funs.add(y -> m.thenCompose(y, new CompletableFutureInc(m))); + + funs.add(y -> CompletableFuture.allOf(y)); + funs.add(y -> CompletableFuture.allOf(y, v42)); + funs.add(y -> CompletableFuture.allOf(v42, y)); + funs.add(y -> CompletableFuture.anyOf(y)); + funs.add(y -> CompletableFuture.anyOf(y, incomplete)); + funs.add(y -> CompletableFuture.anyOf(incomplete, y)); for (Function, CompletableFuture> fun : funs) { @@ -3940,12 +3963,12 @@ public class CompletableFutureTest exten public void testMinimalCompletionStage_minimality() { if (!testImplementationDetails) return; Function toSignature = - (method) -> method.getName() + Arrays.toString(method.getParameterTypes()); + method -> method.getName() + Arrays.toString(method.getParameterTypes()); Predicate isNotStatic = - (method) -> (method.getModifiers() & Modifier.STATIC) == 0; + method -> (method.getModifiers() & Modifier.STATIC) == 0; List minimalMethods = Stream.of(Object.class, CompletionStage.class) - .flatMap((klazz) -> Stream.of(klazz.getMethods())) + .flatMap(klazz -> Stream.of(klazz.getMethods())) .filter(isNotStatic) .collect(Collectors.toList()); // Methods from CompletableFuture permitted NOT to throw UOE @@ -3961,11 +3984,14 @@ public class CompletableFutureTest exten .collect(Collectors.toSet()); List allMethods = Stream.of(CompletableFuture.class.getMethods()) .filter(isNotStatic) - .filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method))) + .filter(method -> !permittedMethodSignatures.contains(toSignature.apply(method))) .collect(Collectors.toList()); List> stages = new ArrayList<>(); - stages.add(new CompletableFuture().minimalCompletionStage()); + CompletionStage min = + new CompletableFuture().minimalCompletionStage(); + stages.add(min); + stages.add(min.thenApply(x -> x)); stages.add(CompletableFuture.completedStage(1)); stages.add(CompletableFuture.failedStage(new CFException())); @@ -4001,6 +4027,131 @@ public class CompletableFutureTest exten throw new Error("Methods did not throw UOE: " + bugs); } + /** + * minimalStage.toCompletableFuture() returns a CompletableFuture that + * is completed normally, with the same value, when source is. + */ + public void testMinimalCompletionStage_toCompletableFuture_normalCompletion() { + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + CompletionStage minimal = f.minimalCompletionStage(); + if (!createIncomplete) assertTrue(f.complete(v1)); + CompletableFuture g = minimal.toCompletableFuture(); + if (createIncomplete) { + checkIncomplete(f); + checkIncomplete(g); + assertTrue(f.complete(v1)); + } + checkCompletedNormally(f, v1); + checkCompletedNormally(g, v1); + }} + + /** + * minimalStage.toCompletableFuture() returns a CompletableFuture that + * is completed exceptionally when source is. + */ + public void testMinimalCompletionStage_toCompletableFuture_exceptionalCompletion() { + for (boolean createIncomplete : new boolean[] { true, false }) + { + CFException ex = new CFException(); + CompletableFuture f = new CompletableFuture<>(); + CompletionStage minimal = f.minimalCompletionStage(); + if (!createIncomplete) f.completeExceptionally(ex); + CompletableFuture g = minimal.toCompletableFuture(); + if (createIncomplete) { + checkIncomplete(f); + checkIncomplete(g); + f.completeExceptionally(ex); + } + checkCompletedExceptionally(f, ex); + checkCompletedWithWrappedException(g, ex); + }} + + /** + * minimalStage.toCompletableFuture() gives mutable CompletableFuture + */ + public void testMinimalCompletionStage_toCompletableFuture_mutable() { + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + CompletionStage minimal = f.minimalCompletionStage(); + CompletableFuture g = minimal.toCompletableFuture(); + assertTrue(g.complete(v1)); + checkCompletedNormally(g, v1); + checkIncomplete(f); + checkIncomplete(minimal.toCompletableFuture()); + }} + + /** + * minimalStage.toCompletableFuture().join() awaits completion + */ + public void testMinimalCompletionStage_toCompletableFuture_join() throws Exception { + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + if (!createIncomplete) assertTrue(f.complete(v1)); + CompletionStage minimal = f.minimalCompletionStage(); + if (createIncomplete) assertTrue(f.complete(v1)); + assertEquals(v1, minimal.toCompletableFuture().join()); + assertEquals(v1, minimal.toCompletableFuture().get()); + checkCompletedNormally(minimal.toCompletableFuture(), v1); + }} + + /** + * Completion of a toCompletableFuture copy of a minimal stage + * does not complete its source. + */ + public void testMinimalCompletionStage_toCompletableFuture_oneWayPropagation() { + CompletableFuture f = new CompletableFuture<>(); + CompletionStage g = f.minimalCompletionStage(); + assertTrue(g.toCompletableFuture().complete(1)); + assertTrue(g.toCompletableFuture().complete(null)); + assertTrue(g.toCompletableFuture().cancel(true)); + assertTrue(g.toCompletableFuture().cancel(false)); + assertTrue(g.toCompletableFuture().completeExceptionally(new CFException())); + checkIncomplete(g.toCompletableFuture()); + f.complete(1); + checkCompletedNormally(g.toCompletableFuture(), 1); + } + + /** Demo utility method for external reliable toCompletableFuture */ + static CompletableFuture toCompletableFuture(CompletionStage stage) { + CompletableFuture f = new CompletableFuture<>(); + stage.handle((T t, Throwable ex) -> { + if (ex != null) f.completeExceptionally(ex); + else f.complete(t); + return null; + }); + return f; + } + + /** Demo utility method to join a CompletionStage */ + static T join(CompletionStage stage) { + return toCompletableFuture(stage).join(); + } + + /** + * Joining a minimal stage "by hand" works + */ + public void testMinimalCompletionStage_join_by_hand() { + for (boolean createIncomplete : new boolean[] { true, false }) + for (Integer v1 : new Integer[] { 1, null }) + { + CompletableFuture f = new CompletableFuture<>(); + CompletionStage minimal = f.minimalCompletionStage(); + CompletableFuture g = new CompletableFuture<>(); + if (!createIncomplete) assertTrue(f.complete(v1)); + minimal.thenAccept(x -> g.complete(x)); + if (createIncomplete) assertTrue(f.complete(v1)); + g.join(); + checkCompletedNormally(g, v1); + checkCompletedNormally(f, v1); + assertEquals(v1, join(minimal)); + }} + static class Monad { static class ZeroException extends RuntimeException { public ZeroException() { super("monadic zero"); } @@ -4017,7 +4168,7 @@ public class CompletableFutureTest exten static Function> compose (Function> f, Function> g) { - return (x) -> f.apply(x).thenCompose(g); + return x -> f.apply(x).thenCompose(g); } static void assertZero(CompletableFuture f) { @@ -4097,9 +4248,9 @@ public class CompletableFutureTest exten // Some mutually non-commutative functions Function> triple - = (x) -> Monad.unit(3 * x); + = x -> Monad.unit(3 * x); Function> inc - = (x) -> Monad.unit(x + 1); + = x -> Monad.unit(x + 1); // unit is a right identity: m >>= unit === m Monad.assertFutureEquals(inc.apply(5L).thenCompose(unit), @@ -4111,7 +4262,7 @@ public class CompletableFutureTest exten // associativity: (m >>= f) >>= g === m >>= ( \x -> (f x >>= g) ) Monad.assertFutureEquals( unit.apply(5L).thenCompose(inc).thenCompose(triple), - unit.apply(5L).thenCompose((x) -> inc.apply(x).thenCompose(triple))); + unit.apply(5L).thenCompose(x -> inc.apply(x).thenCompose(triple))); // The case for CompletableFuture as an additive monad is weaker... @@ -4121,7 +4272,7 @@ public class CompletableFutureTest exten // left zero: zero >>= f === zero Monad.assertZero(zero.thenCompose(inc)); // right zero: f >>= (\x -> zero) === zero - Monad.assertZero(inc.apply(5L).thenCompose((x) -> zero)); + Monad.assertZero(inc.apply(5L).thenCompose(x -> zero)); // f plus zero === f Monad.assertFutureEquals(Monad.unit(5L), @@ -4179,8 +4330,8 @@ public class CompletableFutureTest exten 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.thenAccept(x -> count.getAndIncrement()); + head.thenApply(x -> count.getAndIncrement()); head.runAfterBoth(complete, () -> count.getAndIncrement()); head.thenAcceptBoth(complete, (x, y) -> count.getAndIncrement()); @@ -4190,11 +4341,11 @@ public class CompletableFutureTest exten complete.thenCombine(head, (x, y) -> count.getAndIncrement()); head.runAfterEither(new CompletableFuture(), () -> count.getAndIncrement()); - head.acceptEither(new CompletableFuture(), (x) -> count.getAndIncrement()); - head.applyToEither(new CompletableFuture(), (x) -> count.getAndIncrement()); + head.acceptEither(new CompletableFuture(), x -> count.getAndIncrement()); + head.applyToEither(new CompletableFuture(), x -> count.getAndIncrement()); new CompletableFuture().runAfterEither(head, () -> count.getAndIncrement()); - new CompletableFuture().acceptEither(head, (x) -> count.getAndIncrement()); - new CompletableFuture().applyToEither(head, (x) -> count.getAndIncrement()); + new CompletableFuture().acceptEither(head, x -> count.getAndIncrement()); + new CompletableFuture().applyToEither(head, x -> count.getAndIncrement()); } head.complete(null); assertEquals(5 * 3 * n, count.get()); @@ -4211,11 +4362,11 @@ public class CompletableFutureTest exten f.complete(null); f = new CompletableFuture<>(); - f.acceptEither(incomplete, (x) -> {}); + f.acceptEither(incomplete, x -> {}); f.complete(null); f = new CompletableFuture<>(); - f.applyToEither(incomplete, (x) -> x); + f.applyToEither(incomplete, x -> x); f.complete(null); f = new CompletableFuture<>(); @@ -4229,11 +4380,11 @@ public class CompletableFutureTest exten f.complete(null); f = new CompletableFuture<>(); - incomplete.acceptEither(f, (x) -> {}); + incomplete.acceptEither(f, x -> {}); f.complete(null); f = new CompletableFuture<>(); - incomplete.applyToEither(f, (x) -> x); + incomplete.applyToEither(f, x -> x); f.complete(null); f = new CompletableFuture<>(); @@ -4242,12 +4393,11 @@ public class CompletableFutureTest exten } } - /* - * Tests below currently fail in stress mode due to memory retention. - * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest tck + /** + * Reproduction recipe for: + * 8160402: Garbage retention with CompletableFuture.anyOf + * cvs update -D '2016-05-01' ./src/main/java/util/concurrent/CompletableFuture.java && ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testAnyOfGarbageRetention tck; cvs update -A */ - - /** Checks for garbage retention with anyOf. */ public void testAnyOfGarbageRetention() throws Throwable { for (Integer v : new Integer[] { 1, null }) { @@ -4261,7 +4411,12 @@ public class CompletableFutureTest exten checkCompletedNormally(CompletableFuture.anyOf(fs), v); }} - /** Checks for garbage retention with allOf. */ + /** + * Checks for garbage retention with allOf. + * + * As of 2016-07, fails with OOME: + * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testCancelledAllOfGarbageRetention tck + */ public void testCancelledAllOfGarbageRetention() throws Throwable { final int n = expensiveTests ? 100_000 : 10; CompletableFuture[] fs @@ -4272,6 +4427,37 @@ public class CompletableFutureTest exten assertTrue(CompletableFuture.allOf(fs).cancel(false)); } + /** + * Checks for garbage retention when a dependent future is + * cancelled and garbage-collected. + * 8161600: Garbage retention when source CompletableFutures are never completed + * + * As of 2016-07, fails with OOME: + * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testCancelledGarbageRetention tck + */ + public void testCancelledGarbageRetention() throws Throwable { + final int n = expensiveTests ? 100_000 : 10; + CompletableFuture neverCompleted = new CompletableFuture<>(); + for (int i = 0; i < n; i++) + assertTrue(neverCompleted.thenRun(() -> {}).cancel(true)); + } + + /** + * Checks for garbage retention when MinimalStage.toCompletableFuture() + * is invoked many times. + * 8161600: Garbage retention when source CompletableFutures are never completed + * + * As of 2016-07, fails with OOME: + * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testToCompletableFutureGarbageRetention tck + */ + public void testToCompletableFutureGarbageRetention() throws Throwable { + final int n = expensiveTests ? 900_000 : 10; + CompletableFuture neverCompleted = new CompletableFuture<>(); + CompletionStage minimal = neverCompleted.minimalCompletionStage(); + for (int i = 0; i < n; i++) + assertTrue(minimal.toCompletableFuture().cancel(true)); + } + // static U join(CompletionStage stage) { // CompletableFuture f = new CompletableFuture<>(); // stage.whenComplete((v, ex) -> {