--- jsr166/src/test/tck/CompletableFutureTest.java 2016/06/27 21:17:49 1.158 +++ jsr166/src/test/tck/CompletableFutureTest.java 2016/09/22 22:05:49 1.179 @@ -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; @@ -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; @@ -2700,6 +2699,7 @@ public class CompletableFutureTest exten for (ExecutionMode m : ExecutionMode.values()) for (Integer v1 : new Integer[] { 1, null }) for (Integer v2 : new Integer[] { 2, null }) + for (boolean pushNop : new boolean[] { true, false }) { final CompletableFuture f = new CompletableFuture<>(); final CompletableFuture g = new CompletableFuture<>(); @@ -2712,6 +2712,10 @@ public class CompletableFutureTest exten checkIncomplete(h1); rs[0].assertNotInvoked(); rs[1].assertNotInvoked(); + if (pushNop) { // ad hoc test of intra-completion interference + m.thenRun(f, () -> {}); + m.thenRun(g, () -> {}); + } f.complete(v1); checkCompletedNormally(h0, null); checkCompletedNormally(h1, null); @@ -3354,8 +3358,8 @@ public class CompletableFutureTest exten * Test submissions to an executor that rejects all tasks. */ public void testRejectingExecutor() { - for (Integer v : new Integer[] { 1, null }) { - + for (Integer v : new Integer[] { 1, null }) + { final CountingRejectingExecutor e = new CountingRejectingExecutor(); final CompletableFuture complete = CompletableFuture.completedFuture(v); @@ -3434,9 +3438,7 @@ public class CompletableFutureTest exten checkCompletedWithWrappedException(future, e.ex); assertEquals(futures.size(), e.count.get()); - - } - } + }} /** * Test submissions to an executor that rejects all tasks, but @@ -3444,10 +3446,10 @@ public class CompletableFutureTest exten * explicitly completed. */ public void testRejectingExecutorNeverInvoked() { + for (Integer v : new Integer[] { 1, null }) + { final CountingRejectingExecutor e = new CountingRejectingExecutor(); - for (Integer v : new Integer[] { 1, null }) { - final CompletableFuture complete = CompletableFuture.completedFuture(v); final CompletableFuture incomplete = new CompletableFuture<>(); @@ -3495,9 +3497,7 @@ public class CompletableFutureTest exten checkCompletedNormally(future, null); assertEquals(0, e.count.get()); - - } - } + }} /** * toCompletableFuture returns this CompletableFuture. @@ -3577,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); } /** @@ -3851,19 +3875,23 @@ public class CompletableFutureTest exten final CompletableFuture v42 = CompletableFuture.completedFuture(42); final CompletableFuture incomplete = new CompletableFuture<>(); + final Runnable noopRunnable = new Noop(m); + final Consumer noopConsumer = new NoopConsumer(m); + final Function incFunction = new IncFunction(m); + List, CompletableFuture>> funs = new ArrayList<>(); - funs.add((y) -> m.thenRun(y, new Noop(m))); - funs.add((y) -> m.thenAccept(y, new NoopConsumer(m))); - funs.add((y) -> m.thenApply(y, new IncFunction(m))); - - funs.add((y) -> m.runAfterEither(y, incomplete, new Noop(m))); - funs.add((y) -> m.acceptEither(y, incomplete, new NoopConsumer(m))); - funs.add((y) -> m.applyToEither(y, incomplete, new IncFunction(m))); + 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, new Noop(m))); - funs.add((y) -> m.runAfterBoth(v42, y, new Noop(m))); + 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))); @@ -3873,18 +3901,18 @@ public class CompletableFutureTest exten funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m))); - funs.add((y) -> CompletableFuture.allOf(new CompletableFuture[] {y})); - funs.add((y) -> CompletableFuture.allOf(new CompletableFuture[] {y, v42})); - funs.add((y) -> CompletableFuture.allOf(new CompletableFuture[] {v42, y})); - funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture[] {y})); - funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture[] {y, incomplete})); - funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture[] {incomplete, y})); + 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) { CompletableFuture f = new CompletableFuture<>(); f.completeExceptionally(ex); - CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture src = m.thenApply(f, incFunction); checkCompletedWithWrappedException(src, ex); CompletableFuture dep = fun.apply(src); checkCompletedWithWrappedException(dep, ex); @@ -3894,7 +3922,7 @@ public class CompletableFutureTest exten for (Function, CompletableFuture> fun : funs) { CompletableFuture f = new CompletableFuture<>(); - CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture src = m.thenApply(f, incFunction); CompletableFuture dep = fun.apply(src); f.completeExceptionally(ex); checkCompletedWithWrappedException(src, ex); @@ -3908,7 +3936,7 @@ public class CompletableFutureTest exten CompletableFuture f = new CompletableFuture<>(); f.cancel(mayInterruptIfRunning); checkCancelled(f); - CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture src = m.thenApply(f, incFunction); checkCompletedWithWrappedCancellationException(src); CompletableFuture dep = fun.apply(src); checkCompletedWithWrappedCancellationException(dep); @@ -3919,7 +3947,7 @@ public class CompletableFutureTest exten for (Function, CompletableFuture> fun : funs) { CompletableFuture f = new CompletableFuture<>(); - CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture src = m.thenApply(f, incFunction); CompletableFuture dep = fun.apply(src); f.cancel(mayInterruptIfRunning); checkCancelled(f); @@ -3930,7 +3958,7 @@ public class CompletableFutureTest exten }} /** - * Minimal completion stages throw UOE for all non-CompletionStage methods + * Minimal completion stages throw UOE for most non-CompletionStage methods */ public void testMinimalCompletionStage_minimality() { if (!testImplementationDetails) return; @@ -3959,8 +3987,13 @@ public class CompletableFutureTest exten .filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method))) .collect(Collectors.toList()); - CompletionStage minimalStage = + List> stages = new ArrayList<>(); + 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())); List bugs = new ArrayList<>(); for (Method method : allMethods) { @@ -3976,22 +4009,149 @@ public class CompletableFutureTest exten else if (parameterTypes[i] == long.class) args[i] = 0L; } - try { - method.invoke(minimalStage, args); - bugs.add(method); - } - catch (java.lang.reflect.InvocationTargetException expected) { - if (! (expected.getCause() instanceof UnsupportedOperationException)) { + for (CompletionStage stage : stages) { + try { + method.invoke(stage, args); bugs.add(method); - // expected.getCause().printStackTrace(); } + catch (java.lang.reflect.InvocationTargetException expected) { + if (! (expected.getCause() instanceof UnsupportedOperationException)) { + bugs.add(method); + // expected.getCause().printStackTrace(); + } + } + catch (ReflectiveOperationException bad) { throw new Error(bad); } } - catch (ReflectiveOperationException bad) { throw new Error(bad); } } if (!bugs.isEmpty()) - throw new Error("Methods did not throw UOE: " + bugs.toString()); + 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"); } @@ -4138,12 +4298,33 @@ public class CompletableFutureTest exten Monad.plus(godot, Monad.unit(5L))); } + /** Test long recursive chains of CompletableFutures with cascading completions */ + public void testRecursiveChains() throws Throwable { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean addDeadEnds : new boolean[] { true, false }) + { + final int val = 42; + final int n = expensiveTests ? 1_000 : 2; + CompletableFuture head = new CompletableFuture<>(); + CompletableFuture tail = head; + for (int i = 0; i < n; i++) { + if (addDeadEnds) m.thenApply(tail, v -> v + 1); + tail = m.thenApply(tail, v -> v + 1); + if (addDeadEnds) m.applyToEither(tail, tail, v -> v + 1); + tail = m.applyToEither(tail, tail, v -> v + 1); + if (addDeadEnds) m.thenCombine(tail, tail, (v, w) -> v + 1); + tail = m.thenCombine(tail, tail, (v, w) -> v + 1); + } + head.complete(val); + assertEquals(val + 3 * n, (int) tail.join()); + }} + /** * 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 int n = expensiveTests ? 1_000_000 : 10; final CompletableFuture head = new CompletableFuture<>(); final CompletableFuture complete = CompletableFuture.completedFuture((Void)null); final AtomicInteger count = new AtomicInteger(0); @@ -4170,10 +4351,9 @@ public class CompletableFutureTest exten assertEquals(5 * 3 * n, count.get()); } - /** ant -Dvmoptions=-Xmx8m -Djsr166.tckTestClass=CompletableFutureTest tck */ - public void testCoCompletionGarbage() throws Throwable { - // final int n = 3_000_000; - final int n = 100; + /** ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest tck */ + public void testCoCompletionGarbageRetention() throws Throwable { + final int n = expensiveTests ? 1_000_000 : 10; final CompletableFuture incomplete = new CompletableFuture<>(); CompletableFuture f; for (int i = 0; i < n; i++) { @@ -4213,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 }) { @@ -4232,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 @@ -4243,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) -> {