Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does CompletionStage always wrap exceptions in CompletionException?

Tags:

The CompletionStage Javadoc states:

[...] if a stage's computation terminates abruptly with an (unchecked) exception or error, then all dependent stages requiring its completion complete exceptionally as well, with a CompletionException holding the exception as its cause.

Seeing as exceptional completions always wrap exceptions in CompletionException why do exceptionally(), whenComplete() and handle() represent the exception as Throwable instead of CompletionException?

This matters because it prevents one from directly re-throwing exceptions inside these methods.

Is it possible for these methods to receive an exception other than CompletionException? Or can I safely force a cast to this type?

(I ran some tests locally, as well as dig through the CompletableFuture source-code and, at first glance, I do not see how any other type of exception could be thrown.)

like image 801
Gili Avatar asked Mar 12 '18 08:03

Gili


People also ask

Does CompletableFuture throw exception?

The CompletableFuture. join() method is similar to the get method, but it throws an unchecked exception in case the Future does not complete normally.

What is CompletionException?

Class CompletionExceptionException thrown when an error or other exception is encountered in the course of completing a result or task.


1 Answers

Is it possible for these methods to receive an exception other than CompletionException?

Yes, it is possible and you shouldn't cast to CompletionException without an instanceof check (or a review of your usage).

Take this example

CompletableFuture<Void> root = new CompletableFuture<>(); root.whenComplete((v, t) -> {     System.out.println(t.getClass()); // class java.io.IOException }); root.completeExceptionally(new IOException("blow it up")); 

whenComplete will receive the IOException rather than a CompletionException wrapping it. The same behavior applies to exceptionally and handle.


A stage's computation is defined in the Javadoc:

The computation performed by a stage may be expressed as a Function, Consumer, or Runnable (using methods with names including apply, accept, or run, respectively) depending on whether it requires arguments and/or produces results.

I believe this quote

if a stage's computation terminates abruptly with an (unchecked) exception or error

is referring to one of those Function#apply, Consumer#accept, or Runnable#run methods terminating abruptly because of a thrown exception, not because a stage completed exceptionally through some other mechanism.

Note also that the Javadoc says

This interface does not define methods for initially creating, forcibly completing normally or exceptionally, probing completion status or results, or awaiting completion of a stage. Implementations of CompletionStage may provide means of achieving such effects, as appropriate

In other words, the interface allows implementations to complete stages exceptionally without abruptly terminating any computation. I think this allows for new behavior.


If we extend my example from before

CompletableFuture<Void> root = new CompletableFuture<>(); CompletableFuture<Void> child = root.whenComplete((v, t) -> {     System.out.println(t.getClass()); // class java.io.Exception }); child.whenComplete((v, t) -> {     System.out.println(t.getClass()); // class java.util.concurrent.CompletionException }); root.completeExceptionally(new IOException("blow it up")); 

You'll notice the completion attached to the child receives a CompletionException wrapping the original IOException. This isn't obvious to me from the Javadoc, which states

Returns a new CompletionStage with the same result or exception as this stage

All in all, it seems like the raw exception from a completeExceptionally is passed down to direct dependents, while dependents of dependents receive an enclosing CompletionException.

like image 138
Sotirios Delimanolis Avatar answered Sep 24 '22 03:09

Sotirios Delimanolis