# HG changeset patch # User dl # Date 1507064105 25200 # Node ID 90b7465b9ac705ac739a714240644dbadf1ed1c0 # Parent 62cd7fef87b6b08c5d4ea980584b6f023d6c42e8 8186265: Make toString() methods of "task" objects more useful Reviewed-by: martin, psandoz, rriggs, dholmes, darcy Contributed-by: Charles Munger diff -r 62cd7fef87b6 -r 90b7465b9ac7 src/java.base/share/classes/java/util/concurrent/CompletableFuture.java --- a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Tue Oct 03 13:50:09 2017 -0700 +++ b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Tue Oct 03 13:55:05 2017 -0700 @@ -2490,13 +2490,13 @@ for (Completion p = stack; p != null; p = p.next) ++count; return super.toString() + - ((r == null) ? - ((count == 0) ? - "[Not completed]" : - "[Not completed, " + count + " dependents]") : - (((r instanceof AltResult) && ((AltResult)r).ex != null) ? - "[Completed exceptionally]" : - "[Completed normally]")); + ((r == null) + ? ((count == 0) + ? "[Not completed]" + : "[Not completed, " + count + " dependents]") + : (((r instanceof AltResult) && ((AltResult)r).ex != null) + ? "[Completed exceptionally: " + ((AltResult)r).ex + "]" + : "[Completed normally]")); } // jdk9 additions diff -r 62cd7fef87b6 -r 90b7465b9ac7 src/java.base/share/classes/java/util/concurrent/Executors.java --- a/src/java.base/share/classes/java/util/concurrent/Executors.java Tue Oct 03 13:50:09 2017 -0700 +++ b/src/java.base/share/classes/java/util/concurrent/Executors.java Tue Oct 03 13:55:05 2017 -0700 @@ -514,6 +514,9 @@ task.run(); return result; } + public String toString() { + return super.toString() + "[Wrapped task = " + task + "]"; + } } /** @@ -540,6 +543,10 @@ throw e.getException(); } } + + public String toString() { + return super.toString() + "[Wrapped task = " + task + "]"; + } } /** @@ -592,6 +599,10 @@ throw e.getException(); } } + + public String toString() { + return super.toString() + "[Wrapped task = " + task + "]"; + } } /** diff -r 62cd7fef87b6 -r 90b7465b9ac7 src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java Tue Oct 03 13:50:09 2017 -0700 +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java Tue Oct 03 13:55:05 2017 -0700 @@ -1375,6 +1375,9 @@ public final void setRawResult(T v) { result = v; } public final boolean exec() { runnable.run(); return true; } public final void run() { invoke(); } + public String toString() { + return super.toString() + "[Wrapped task = " + runnable + "]"; + } private static final long serialVersionUID = 5232453952276885070L; } @@ -1392,6 +1395,9 @@ public final void setRawResult(Void v) { } public final boolean exec() { runnable.run(); return true; } public final void run() { invoke(); } + public String toString() { + return super.toString() + "[Wrapped task = " + runnable + "]"; + } private static final long serialVersionUID = 5232453952276885070L; } @@ -1437,6 +1443,9 @@ } } public final void run() { invoke(); } + public String toString() { + return super.toString() + "[Wrapped task = " + callable + "]"; + } private static final long serialVersionUID = 2838392045355241008L; } diff -r 62cd7fef87b6 -r 90b7465b9ac7 src/java.base/share/classes/java/util/concurrent/FutureTask.java --- a/src/java.base/share/classes/java/util/concurrent/FutureTask.java Tue Oct 03 13:50:09 2017 -0700 +++ b/src/java.base/share/classes/java/util/concurrent/FutureTask.java Tue Oct 03 13:55:05 2017 -0700 @@ -480,6 +480,41 @@ } } + /** + * Returns a string representation of this FutureTask. + * + * @implSpec + * The default implementation returns a string identifying this + * FutureTask, as well as its completion state. The state, in + * brackets, contains one of the strings {@code "Completed Normally"}, + * {@code "Completed Exceptionally"}, {@code "Cancelled"}, or {@code + * "Not completed"}. + * + * @return a string representation of this FutureTask + */ + public String toString() { + final String status; + switch (state) { + case NORMAL: + status = "[Completed normally]"; + break; + case EXCEPTIONAL: + status = "[Completed exceptionally: " + outcome + "]"; + break; + case CANCELLED: + case INTERRUPTING: + case INTERRUPTED: + status = "[Cancelled]"; + break; + default: + final Callable callable = this.callable; + status = (callable == null) + ? "[Not completed]" + : "[Not completed, task = " + callable + "]"; + } + return super.toString() + status; + } + // VarHandle mechanics private static final VarHandle STATE; private static final VarHandle RUNNER; diff -r 62cd7fef87b6 -r 90b7465b9ac7 test/jdk/java/util/concurrent/CompletableFuture/Basic.java --- a/test/jdk/java/util/concurrent/CompletableFuture/Basic.java Tue Oct 03 13:50:09 2017 -0700 +++ b/test/jdk/java/util/concurrent/CompletableFuture/Basic.java Tue Oct 03 13:55:05 2017 -0700 @@ -74,7 +74,7 @@ check(!cf.isCompletedExceptionally(), "Expected isCompletedExceptionally to return false"); check(!cf.isCancelled(), "Expected isCancelled to be false"); check(!cf.cancel(true), "Expected cancel to return false"); - check(cf.toString().contains("[Completed normally]")); + check(cf.toString().matches(".*\\[.*Completed normally.*\\]")); check(cf.complete(null) == false, "Expected complete() to fail"); check(cf.completeExceptionally(new Throwable()) == false, "Expected completeExceptionally() to fail"); @@ -106,7 +106,7 @@ check(cf.isCompletedExceptionally(), "Expected isCompletedExceptionally"); check(cf.isCancelled() == cancelled, "Expected isCancelled: " + cancelled + ", got:" + cf.isCancelled()); check(cf.cancel(true) == cancelled, "Expected cancel: " + cancelled + ", got:" + cf.cancel(true)); - check(cf.toString().contains("[Completed exceptionally]")); // ## TODO: 'E'xceptionally + check(cf.toString().matches(".*\\[.*Completed exceptionally.*\\]")); // ## TODO: 'E'xceptionally check(cf.complete((T)new Object()) == false, "Expected complete() to fail"); check(cf.completeExceptionally(new Throwable()) == false, "Expected completeExceptionally() to fail, already completed"); diff -r 62cd7fef87b6 -r 90b7465b9ac7 test/jdk/java/util/concurrent/tck/CompletableFutureTest.java --- a/test/jdk/java/util/concurrent/tck/CompletableFutureTest.java Tue Oct 03 13:50:09 2017 -0700 +++ b/test/jdk/java/util/concurrent/tck/CompletableFutureTest.java Tue Oct 03 13:55:05 2017 -0700 @@ -86,7 +86,7 @@ void checkIncomplete(CompletableFuture f) { assertFalse(f.isDone()); assertFalse(f.isCancelled()); - assertTrue(f.toString().contains("Not completed")); + assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]")); try { assertNull(f.getNow(null)); } catch (Throwable fail) { threadUnexpectedException(fail); } @@ -109,7 +109,7 @@ assertTrue(f.isDone()); assertFalse(f.isCancelled()); assertFalse(f.isCompletedExceptionally()); - assertTrue(f.toString().contains("[Completed normally]")); + assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]")); } /** @@ -165,7 +165,7 @@ assertFalse(f.isCancelled()); assertTrue(f.isDone()); assertTrue(f.isCompletedExceptionally()); - assertTrue(f.toString().contains("[Completed exceptionally]")); + assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); } void checkCompletedWithWrappedCFException(CompletableFuture f) { @@ -220,7 +220,7 @@ assertTrue(f.isDone()); assertTrue(f.isCompletedExceptionally()); assertTrue(f.isCancelled()); - assertTrue(f.toString().contains("[Completed exceptionally]")); + assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); } /** @@ -356,23 +356,40 @@ /** * toString indicates current completion state */ - public void testToString() { - CompletableFuture f; - - f = new CompletableFuture(); - assertTrue(f.toString().contains("[Not completed]")); - + public void testToString_incomplete() { + CompletableFuture f = new CompletableFuture(); + assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]")); + if (testImplementationDetails) + assertEquals(identityString(f) + "[Not completed]", + f.toString()); + } + + public void testToString_normal() { + CompletableFuture f = new CompletableFuture(); assertTrue(f.complete("foo")); - assertTrue(f.toString().contains("[Completed normally]")); - - f = new CompletableFuture(); + assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]")); + if (testImplementationDetails) + assertEquals(identityString(f) + "[Completed normally]", + f.toString()); + } + + public void testToString_exception() { + CompletableFuture f = new CompletableFuture(); assertTrue(f.completeExceptionally(new IndexOutOfBoundsException())); - assertTrue(f.toString().contains("[Completed exceptionally]")); - + assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); + if (testImplementationDetails) + assertTrue(f.toString().startsWith( + identityString(f) + "[Completed exceptionally: ")); + } + + public void testToString_cancelled() { for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { - f = new CompletableFuture(); + CompletableFuture f = new CompletableFuture(); assertTrue(f.cancel(mayInterruptIfRunning)); - assertTrue(f.toString().contains("[Completed exceptionally]")); + assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); + if (testImplementationDetails) + assertTrue(f.toString().startsWith( + identityString(f) + "[Completed exceptionally: ")); } } diff -r 62cd7fef87b6 -r 90b7465b9ac7 test/jdk/java/util/concurrent/tck/ExecutorsTest.java --- a/test/jdk/java/util/concurrent/tck/ExecutorsTest.java Tue Oct 03 13:50:09 2017 -0700 +++ b/test/jdk/java/util/concurrent/tck/ExecutorsTest.java Tue Oct 03 13:55:05 2017 -0700 @@ -613,4 +613,56 @@ } catch (NullPointerException success) {} } + /** + * callable(runnable, x).toString() contains toString of wrapped task + */ + public void testCallable_withResult_toString() { + if (testImplementationDetails) { + Runnable r = () -> {}; + Callable c = Executors.callable(r, ""); + assertEquals( + identityString(c) + "[Wrapped task = " + r.toString() + "]", + c.toString()); + } + } + + /** + * callable(runnable).toString() contains toString of wrapped task + */ + public void testCallable_toString() { + if (testImplementationDetails) { + Runnable r = () -> {}; + Callable c = Executors.callable(r); + assertEquals( + identityString(c) + "[Wrapped task = " + r.toString() + "]", + c.toString()); + } + } + + /** + * privilegedCallable(callable).toString() contains toString of wrapped task + */ + public void testPrivilegedCallable_toString() { + if (testImplementationDetails) { + Callable c = () -> ""; + Callable priv = Executors.privilegedCallable(c); + assertEquals( + identityString(priv) + "[Wrapped task = " + c.toString() + "]", + priv.toString()); + } + } + + /** + * privilegedCallableUsingCurrentClassLoader(callable).toString() + * contains toString of wrapped task + */ + public void testPrivilegedCallableUsingCurrentClassLoader_toString() { + if (testImplementationDetails) { + Callable c = () -> ""; + Callable priv = Executors.privilegedCallableUsingCurrentClassLoader(c); + assertEquals( + identityString(priv) + "[Wrapped task = " + c.toString() + "]", + priv.toString()); + } + } } diff -r 62cd7fef87b6 -r 90b7465b9ac7 test/jdk/java/util/concurrent/tck/ForkJoinTaskTest.java --- a/test/jdk/java/util/concurrent/tck/ForkJoinTaskTest.java Tue Oct 03 13:50:09 2017 -0700 +++ b/test/jdk/java/util/concurrent/tck/ForkJoinTaskTest.java Tue Oct 03 13:55:05 2017 -0700 @@ -35,6 +35,7 @@ import java.util.Arrays; import java.util.HashSet; +import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; @@ -1675,4 +1676,42 @@ testInvokeOnPool(mainPool(), a); } + /** + * adapt(runnable).toString() contains toString of wrapped task + */ + public void testAdapt_Runnable_toString() { + if (testImplementationDetails) { + Runnable r = () -> {}; + ForkJoinTask task = ForkJoinTask.adapt(r); + assertEquals( + identityString(task) + "[Wrapped task = " + r.toString() + "]", + task.toString()); + } + } + + /** + * adapt(runnable, x).toString() contains toString of wrapped task + */ + public void testAdapt_Runnable_withResult_toString() { + if (testImplementationDetails) { + Runnable r = () -> {}; + ForkJoinTask task = ForkJoinTask.adapt(r, ""); + assertEquals( + identityString(task) + "[Wrapped task = " + r.toString() + "]", + task.toString()); + } + } + + /** + * adapt(callable).toString() contains toString of wrapped task + */ + public void testAdapt_Callable_toString() { + if (testImplementationDetails) { + Callable c = () -> ""; + ForkJoinTask task = ForkJoinTask.adapt(c); + assertEquals( + identityString(task) + "[Wrapped task = " + c.toString() + "]", + task.toString()); + } + } } diff -r 62cd7fef87b6 -r 90b7465b9ac7 test/jdk/java/util/concurrent/tck/FutureTaskTest.java --- a/test/jdk/java/util/concurrent/tck/FutureTaskTest.java Tue Oct 03 13:50:09 2017 -0700 +++ b/test/jdk/java/util/concurrent/tck/FutureTaskTest.java Tue Oct 03 13:55:05 2017 -0700 @@ -861,4 +861,45 @@ } } + /** + * toString indicates current completion state + */ + public void testToString_incomplete() { + FutureTask f = new FutureTask(() -> ""); + assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]")); + if (testImplementationDetails) + assertTrue(f.toString().startsWith( + identityString(f) + "[Not completed, task =")); + } + + public void testToString_normal() { + FutureTask f = new FutureTask(() -> ""); + f.run(); + assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]")); + if (testImplementationDetails) + assertEquals(identityString(f) + "[Completed normally]", + f.toString()); + } + + public void testToString_exception() { + FutureTask f = new FutureTask( + () -> { throw new ArithmeticException(); }); + f.run(); + assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); + if (testImplementationDetails) + assertTrue(f.toString().startsWith( + identityString(f) + "[Completed exceptionally: ")); + } + + public void testToString_cancelled() { + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { + FutureTask f = new FutureTask(() -> ""); + assertTrue(f.cancel(mayInterruptIfRunning)); + assertTrue(f.toString().matches(".*\\[.*Cancelled.*\\]")); + if (testImplementationDetails) + assertEquals(identityString(f) + "[Cancelled]", + f.toString()); + } + } + } diff -r 62cd7fef87b6 -r 90b7465b9ac7 test/jdk/java/util/concurrent/tck/JSR166TestCase.java --- a/test/jdk/java/util/concurrent/tck/JSR166TestCase.java Tue Oct 03 13:50:09 2017 -0700 +++ b/test/jdk/java/util/concurrent/tck/JSR166TestCase.java Tue Oct 03 13:55:05 2017 -0700 @@ -571,6 +571,7 @@ "DoubleAdderTest", "ForkJoinPool8Test", "ForkJoinTask8Test", + "HashMapTest", "LinkedBlockingDeque8Test", "LinkedBlockingQueue8Test", "LongAccumulatorTest", @@ -1940,6 +1941,18 @@ Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current()); } + /** + * Returns the same String as would be returned by {@link + * Object#toString}, whether or not the given object's class + * overrides toString(). + * + * @see System#identityHashCode + */ + static String identityString(Object x) { + return x.getClass().getName() + + "@" + Integer.toHexString(System.identityHashCode(x)); + } + // --- Shared assertions for Executor tests --- /**