--- jsr166/src/test/tck/CompletableFutureTest.java 2016/06/26 19:17:08 1.153 +++ jsr166/src/test/tck/CompletableFutureTest.java 2016/07/03 19:38:19 1.166 @@ -550,6 +550,15 @@ public class CompletableFutureTest exten } } + static class CountingRejectingExecutor implements Executor { + final RejectedExecutionException ex = new RejectedExecutionException(); + final AtomicInteger count = new AtomicInteger(0); + public void execute(Runnable r) { + count.getAndIncrement(); + throw ex; + } + } + // Used for explicit executor tests static final class ThreadExecutor implements Executor { final AtomicInteger count = new AtomicInteger(0); @@ -1234,6 +1243,18 @@ public class CompletableFutureTest exten r.assertInvoked(); }} + public void testRunAsync_rejectingExecutor() { + CountingRejectingExecutor e = new CountingRejectingExecutor(); + try { + CompletableFuture.runAsync(() -> {}, e); + shouldThrow(); + } catch (Throwable t) { + assertSame(e.ex, t); + } + + assertEquals(1, e.count.get()); + } + /** * supplyAsync completes with result of supplier */ @@ -1268,6 +1289,18 @@ public class CompletableFutureTest exten r.assertInvoked(); }} + public void testSupplyAsync_rejectingExecutor() { + CountingRejectingExecutor e = new CountingRejectingExecutor(); + try { + CompletableFuture.supplyAsync(() -> null, e); + shouldThrow(); + } catch (Throwable t) { + assertSame(e.ex, t); + } + + assertEquals(1, e.count.get()); + } + // seq completion methods /** @@ -2667,6 +2700,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<>(); @@ -2679,6 +2713,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); @@ -3321,10 +3359,9 @@ public class CompletableFutureTest exten * Test submissions to an executor that rejects all tasks. */ public void testRejectingExecutor() { - final RejectedExecutionException ex = new RejectedExecutionException(); - final Executor e = (Runnable r) -> { throw ex; }; - - 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); final CompletableFuture incomplete = new CompletableFuture<>(); @@ -3355,7 +3392,7 @@ public class CompletableFutureTest exten for (CompletableFuture future : fs) { if (src.isDone()) - checkCompletedWithWrappedException(future, ex); + checkCompletedWithWrappedException(future, e.ex); else checkIncomplete(future); } @@ -3392,16 +3429,17 @@ public class CompletableFutureTest exten fs.add(incomplete.runAfterEitherAsync(complete, () -> {}, e)); for (CompletableFuture future : fs) - checkCompletedWithWrappedException(future, ex); + checkCompletedWithWrappedException(future, e.ex); futures.addAll(fs); } incomplete.complete(v); for (CompletableFuture future : futures) - checkCompletedWithWrappedException(future, ex); - } - } + checkCompletedWithWrappedException(future, e.ex); + + assertEquals(futures.size(), e.count.get()); + }} /** * Test submissions to an executor that rejects all tasks, but @@ -3409,18 +3447,10 @@ public class CompletableFutureTest exten * explicitly completed. */ public void testRejectingExecutorNeverInvoked() { - final RejectedExecutionException ex = new RejectedExecutionException(); - class CountingRejectingExecutor implements Executor { - final AtomicInteger count = new AtomicInteger(0); - public void execute(Runnable r) { - count.getAndIncrement(); - throw ex; - } - } + 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<>(); @@ -3468,9 +3498,7 @@ public class CompletableFutureTest exten checkCompletedNormally(future, null); assertEquals(0, e.count.get()); - - } - } + }} /** * toCompletableFuture returns this CompletableFuture. @@ -3824,19 +3852,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))); @@ -3846,18 +3878,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); @@ -3867,7 +3899,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); @@ -3881,7 +3913,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); @@ -3892,7 +3924,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); @@ -3903,7 +3935,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; @@ -3932,8 +3964,10 @@ public class CompletableFutureTest exten .filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method))) .collect(Collectors.toList()); - CompletionStage minimalStage = - new CompletableFuture().minimalCompletionStage(); + List> stages = new ArrayList<>(); + stages.add(new CompletableFuture().minimalCompletionStage()); + stages.add(CompletableFuture.completedStage(1)); + stages.add(CompletableFuture.failedStage(new CFException())); List bugs = new ArrayList<>(); for (Method method : allMethods) { @@ -3949,20 +3983,22 @@ 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); } static class Monad { @@ -4111,12 +4147,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); @@ -4143,6 +4200,78 @@ public class CompletableFutureTest exten assertEquals(5 * 3 * n, count.get()); } + /** 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++) { + f = new CompletableFuture<>(); + f.runAfterEither(incomplete, () -> {}); + f.complete(null); + + f = new CompletableFuture<>(); + f.acceptEither(incomplete, (x) -> {}); + f.complete(null); + + f = new CompletableFuture<>(); + f.applyToEither(incomplete, (x) -> x); + f.complete(null); + + f = new CompletableFuture<>(); + CompletableFuture.anyOf(new CompletableFuture[] { f, incomplete }); + f.complete(null); + } + + for (int i = 0; i < n; i++) { + f = new CompletableFuture<>(); + incomplete.runAfterEither(f, () -> {}); + f.complete(null); + + f = new CompletableFuture<>(); + incomplete.acceptEither(f, (x) -> {}); + f.complete(null); + + f = new CompletableFuture<>(); + incomplete.applyToEither(f, (x) -> x); + f.complete(null); + + f = new CompletableFuture<>(); + CompletableFuture.anyOf(new CompletableFuture[] { incomplete, f }); + f.complete(null); + } + } + + /* + * Tests below currently fail in stress mode due to memory retention. + * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest tck + */ + + /** Checks for garbage retention with anyOf. */ + public void testAnyOfGarbageRetention() throws Throwable { + for (Integer v : new Integer[] { 1, null }) + { + final int n = expensiveTests ? 100_000 : 10; + CompletableFuture[] fs + = (CompletableFuture[]) new CompletableFuture[100]; + for (int i = 0; i < fs.length; i++) + fs[i] = new CompletableFuture<>(); + fs[fs.length - 1].complete(v); + for (int i = 0; i < n; i++) + checkCompletedNormally(CompletableFuture.anyOf(fs), v); + }} + + /** Checks for garbage retention with allOf. */ + public void testCancelledAllOfGarbageRetention() throws Throwable { + final int n = expensiveTests ? 100_000 : 10; + CompletableFuture[] fs + = (CompletableFuture[]) new CompletableFuture[100]; + for (int i = 0; i < fs.length; i++) + fs[i] = new CompletableFuture<>(); + for (int i = 0; i < n; i++) + assertTrue(CompletableFuture.allOf(fs).cancel(false)); + } + // static U join(CompletionStage stage) { // CompletableFuture f = new CompletableFuture<>(); // stage.whenComplete((v, ex) -> {