mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8312436: CompletableFuture never completes when 'Throwable.toString()' method throws Exception
Reviewed-by: alanb
This commit is contained in:
parent
9a8096feb8
commit
326dbb1b13
2 changed files with 61 additions and 11 deletions
|
@ -306,13 +306,57 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||||
return RESULT.compareAndSet(this, null, (t == null) ? NIL : 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
|
* Returns the encoding of the given (non-null) exception as a
|
||||||
* wrapped CompletionException unless it is one already.
|
* wrapped CompletionException unless it is one already.
|
||||||
*/
|
*/
|
||||||
static AltResult encodeThrowable(Throwable x) {
|
static AltResult encodeThrowable(Throwable x) {
|
||||||
return new AltResult((x instanceof CompletionException) ? x :
|
return new AltResult((x instanceof CompletionException) ? x :
|
||||||
new CompletionException(x));
|
wrapInCompletionException(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Completes with an exceptional result, unless already completed. */
|
/** 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) {
|
static Object encodeThrowable(Throwable x, Object r) {
|
||||||
if (!(x instanceof CompletionException))
|
if (!(x instanceof CompletionException))
|
||||||
x = new CompletionException(x);
|
x = wrapInCompletionException(x);
|
||||||
else if (r instanceof AltResult && x == ((AltResult)r).ex)
|
else if (r instanceof AltResult && x == ((AltResult)r).ex)
|
||||||
return r;
|
return r;
|
||||||
return new AltResult(x);
|
return new AltResult(x);
|
||||||
|
@ -365,7 +409,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||||
if (r instanceof AltResult
|
if (r instanceof AltResult
|
||||||
&& (x = ((AltResult)r).ex) != null
|
&& (x = ((AltResult)r).ex) != null
|
||||||
&& !(x instanceof CompletionException))
|
&& !(x instanceof CompletionException))
|
||||||
r = new AltResult(new CompletionException(x));
|
r = new AltResult(wrapInCompletionException(x));
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +437,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||||
if ((x instanceof CompletionException) &&
|
if ((x instanceof CompletionException) &&
|
||||||
(cause = x.getCause()) != null)
|
(cause = x.getCause()) != null)
|
||||||
x = cause;
|
x = cause;
|
||||||
throw new ExecutionException(x);
|
throw wrapInExecutionException(x);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -410,7 +454,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||||
throw new CancellationException(details, (CancellationException)x);
|
throw new CancellationException(details, (CancellationException)x);
|
||||||
if (x instanceof CompletionException)
|
if (x instanceof CompletionException)
|
||||||
throw (CompletionException)x;
|
throw (CompletionException)x;
|
||||||
throw new CompletionException(x);
|
throw wrapInCompletionException(x);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -2605,8 +2649,8 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||||
/**
|
/**
|
||||||
* Returns a string identifying this CompletableFuture, as well as
|
* Returns a string identifying this CompletableFuture, as well as
|
||||||
* its completion state. The state, in brackets, contains the
|
* its completion state. The state, in brackets, contains the
|
||||||
* String {@code "Completed Normally"} or the String {@code
|
* String {@code "Completed normally"} or the String {@code
|
||||||
* "Completed Exceptionally"}, or the String {@code "Not
|
* "Completed exceptionally"}, or the String {@code "Not
|
||||||
* completed"} followed by the number of CompletableFutures
|
* completed"} followed by the number of CompletableFutures
|
||||||
* dependent upon its completion, if any.
|
* dependent upon its completion, if any.
|
||||||
*
|
*
|
||||||
|
@ -2623,7 +2667,7 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
|
||||||
? "[Not completed]"
|
? "[Not completed]"
|
||||||
: "[Not completed, " + count + " dependents]")
|
: "[Not completed, " + count + " dependents]")
|
||||||
: (((r instanceof AltResult) && ((AltResult)r).ex != null)
|
: (((r instanceof AltResult) && ((AltResult)r).ex != null)
|
||||||
? "[Completed exceptionally: " + ((AltResult)r).ex + "]"
|
? "[Completed exceptionally: " + ((AltResult)r).ex.getClass().getName() + "]"
|
||||||
: "[Completed normally]"));
|
: "[Completed normally]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,14 @@ public class CompletableFutureTest extends JSR166TestCase {
|
||||||
return new TestSuite(CompletableFutureTest.class);
|
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) {
|
void checkIncomplete(CompletableFuture<?> f) {
|
||||||
assertFalse(f.isDone());
|
assertFalse(f.isDone());
|
||||||
|
@ -272,8 +279,8 @@ public class CompletableFutureTest extends JSR166TestCase {
|
||||||
*/
|
*/
|
||||||
public void testCompleteExceptionally() {
|
public void testCompleteExceptionally() {
|
||||||
CompletableFuture<Item> f = new CompletableFuture<>();
|
CompletableFuture<Item> f = new CompletableFuture<>();
|
||||||
CFException ex = new CFException();
|
|
||||||
checkIncomplete(f);
|
checkIncomplete(f);
|
||||||
|
CFException ex = new CFException();
|
||||||
f.completeExceptionally(ex);
|
f.completeExceptionally(ex);
|
||||||
checkCompletedExceptionally(f, ex);
|
checkCompletedExceptionally(f, ex);
|
||||||
}
|
}
|
||||||
|
@ -5142,5 +5149,4 @@ public class CompletableFutureTest extends JSR166TestCase {
|
||||||
checkCompletedWithWrappedException(g.toCompletableFuture(), r.ex);
|
checkCompletedWithWrappedException(g.toCompletableFuture(), r.ex);
|
||||||
r.assertInvoked();
|
r.assertInvoked();
|
||||||
}}
|
}}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue