8312436: CompletableFuture never completes when 'Throwable.toString()' method throws Exception
Reviewed-by: alanb
This commit is contained in:
parent
9a8096feb8
commit
326dbb1b13
src/java.base/share/classes/java/util/concurrent
test/jdk/java/util/concurrent/tck
@ -306,13 +306,57 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||
return RESULT.compareAndSet(this, null, (t == null) ? NIL : t);
|
||||
}
|
||||
|
||||
static CompletionException wrapInCompletionException(Throwable t) {
|
||||
if (t == null)
|
||||
return new CompletionException();
|
||||
|
||||
String message;
|
||||
Throwable suppressed;
|
||||
try {
|
||||
message = t.toString();
|
||||
suppressed = null;
|
||||
} catch (Throwable unknown) {
|
||||
message = "";
|
||||
suppressed = unknown;
|
||||
}
|
||||
|
||||
final CompletionException wrapping = new CompletionException(message, t);
|
||||
|
||||
if (suppressed != null)
|
||||
wrapping.addSuppressed(suppressed);
|
||||
|
||||
return wrapping;
|
||||
}
|
||||
|
||||
static ExecutionException wrapInExecutionException(Throwable t) {
|
||||
if (t == null)
|
||||
return new ExecutionException();
|
||||
|
||||
String message;
|
||||
Throwable suppressed;
|
||||
try {
|
||||
message = t.toString();
|
||||
suppressed = null;
|
||||
} catch (Throwable unknown) {
|
||||
message = "";
|
||||
suppressed = unknown;
|
||||
}
|
||||
|
||||
final ExecutionException wrapping = new ExecutionException(message, t);
|
||||
|
||||
if (suppressed != null)
|
||||
wrapping.addSuppressed(suppressed);
|
||||
|
||||
return wrapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoding of the given (non-null) exception as a
|
||||
* wrapped CompletionException unless it is one already.
|
||||
*/
|
||||
static AltResult encodeThrowable(Throwable x) {
|
||||
return new AltResult((x instanceof CompletionException) ? x :
|
||||
new CompletionException(x));
|
||||
wrapInCompletionException(x));
|
||||
}
|
||||
|
||||
/** Completes with an exceptional result, unless already completed. */
|
||||
@ -329,7 +373,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||
*/
|
||||
static Object encodeThrowable(Throwable x, Object r) {
|
||||
if (!(x instanceof CompletionException))
|
||||
x = new CompletionException(x);
|
||||
x = wrapInCompletionException(x);
|
||||
else if (r instanceof AltResult && x == ((AltResult)r).ex)
|
||||
return r;
|
||||
return new AltResult(x);
|
||||
@ -365,7 +409,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||
if (r instanceof AltResult
|
||||
&& (x = ((AltResult)r).ex) != null
|
||||
&& !(x instanceof CompletionException))
|
||||
r = new AltResult(new CompletionException(x));
|
||||
r = new AltResult(wrapInCompletionException(x));
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -393,7 +437,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||
if ((x instanceof CompletionException) &&
|
||||
(cause = x.getCause()) != null)
|
||||
x = cause;
|
||||
throw new ExecutionException(x);
|
||||
throw wrapInExecutionException(x);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@ -410,7 +454,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||
throw new CancellationException(details, (CancellationException)x);
|
||||
if (x instanceof CompletionException)
|
||||
throw (CompletionException)x;
|
||||
throw new CompletionException(x);
|
||||
throw wrapInCompletionException(x);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@ -2605,8 +2649,8 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||
/**
|
||||
* Returns a string identifying this CompletableFuture, as well as
|
||||
* its completion state. The state, in brackets, contains the
|
||||
* String {@code "Completed Normally"} or the String {@code
|
||||
* "Completed Exceptionally"}, or the String {@code "Not
|
||||
* String {@code "Completed normally"} or the String {@code
|
||||
* "Completed exceptionally"}, or the String {@code "Not
|
||||
* completed"} followed by the number of CompletableFutures
|
||||
* dependent upon its completion, if any.
|
||||
*
|
||||
@ -2623,7 +2667,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||
? "[Not completed]"
|
||||
: "[Not completed, " + count + " dependents]")
|
||||
: (((r instanceof AltResult) && ((AltResult)r).ex != null)
|
||||
? "[Completed exceptionally: " + ((AltResult)r).ex + "]"
|
||||
? "[Completed exceptionally: " + ((AltResult)r).ex.getClass().getName() + "]"
|
||||
: "[Completed normally]"));
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,14 @@ public class CompletableFutureTest extends JSR166TestCase {
|
||||
return new TestSuite(CompletableFutureTest.class);
|
||||
}
|
||||
|
||||
static class CFException extends RuntimeException {}
|
||||
static class CFException extends RuntimeException {
|
||||
// This makes sure that CompletableFuture still behaves appropriately
|
||||
// even if thrown exceptions end up throwing exceptions from their String
|
||||
// representations.
|
||||
@Override public String getMessage() {
|
||||
throw new IllegalStateException("malformed");
|
||||
}
|
||||
}
|
||||
|
||||
void checkIncomplete(CompletableFuture<?> f) {
|
||||
assertFalse(f.isDone());
|
||||
@ -272,8 +279,8 @@ public class CompletableFutureTest extends JSR166TestCase {
|
||||
*/
|
||||
public void testCompleteExceptionally() {
|
||||
CompletableFuture<Item> f = new CompletableFuture<>();
|
||||
CFException ex = new CFException();
|
||||
checkIncomplete(f);
|
||||
CFException ex = new CFException();
|
||||
f.completeExceptionally(ex);
|
||||
checkCompletedExceptionally(f, ex);
|
||||
}
|
||||
@ -5142,5 +5149,4 @@ public class CompletableFutureTest extends JSR166TestCase {
|
||||
checkCompletedWithWrappedException(g.toCompletableFuture(), r.ex);
|
||||
r.assertInvoked();
|
||||
}}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user