[concurrency-interest] Exception handling with CompletionStage

Dávid Karnok akarnokd at gmail.com
Wed Jan 17 15:47:32 EST 2018


I can't answer the why, but I have experience with how.

In my recent async-enumerable library, (anticipating an async-await world
:) I used CompletionStage as the means to communicate the next value is
ready. This wrapping behavior forced me to use a helper CompletableFuture
at processing stages as simply forwarding with a whenComplete or the other
CompletionStage operators made the errors wrapped:

public CompletionStage<Boolean> moveNext() {
   CompletableFuture<Boolean> cf = new CompletableFuture<>();

   source.moveNext().whenComplete((hasValue, error) -> {
      if (error != null) {
         cf.completeExceptionally(error);
         return;
      }

      if (hasValue) {
         current = mapperFunction.apply(source.current());
         cf.complete(true);
      } else {
         cf.complete(false);
      }
   });

   return cf;
}



2018-01-17 21:28 GMT+01:00 Pavel Rappo via Concurrency-interest <
concurrency-interest at cs.oswego.edu>:

> Hello,
>
> I have a question regarding a case I ran into while handling an exception
> relayed through a sequence of stages. Consider the following scenarios:
>
> 1.
>
>     public static void main(String[] args) {
>         CompletableFuture.failedFuture(new RuntimeException("hello")).
> join();
>     }
>
> -- stdout --
> Exception in thread "main" java.util.concurrent.CompletionException:
> java.lang.RuntimeException: hello
>    at java.base/java.util.concurrent.CompletableFuture.
> reportJoin(CompletableFuture.java:412)
>    at java.base/java.util.concurrent.CompletableFuture.
> join(CompletableFuture.java:2044)
>    at CF.main(CF.java:6)
> Caused by: java.lang.RuntimeException: hello
>    ... 1 more
>
> 2.
>
>     public static void main(String[] args) {
>         CompletableFuture.failedFuture(new RuntimeException("hello"))
>                 .whenComplete((r, e) -> System.out.println("Error: " + e))
>                 .join();
>     }
>
> -- stdout --
>
> Error: java.lang.RuntimeException: hello
> Exception in thread "main" java.util.concurrent.CompletionException:
> java.lang.RuntimeException: hello
>    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(
> CompletableFuture.java:331)
>    at java.base/java.util.concurrent.CompletableFuture.completeThrowable(
> CompletableFuture.java:346)
>    at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(
> CompletableFuture.java:870)
>    at java.base/java.util.concurrent.CompletableFuture.
> uniWhenCompleteStage(CompletableFuture.java:883)
>    at java.base/java.util.concurrent.CompletableFuture.whenComplete(
> CompletableFuture.java:2251)
>    at CF.main(CF.java:7)
> Caused by: java.lang.RuntimeException: hello
>    at CF.main(CF.java:6)
>
> 3.
>
>     public static void main(String[] args) {
>         CompletableFuture.failedFuture(new RuntimeException("hello"))
>                 .whenComplete((r, e) -> System.out.println("Error 1: " +
> e))
>                 .whenComplete((r, e) -> System.out.println("Error 2: " +
> e))
>                 .join();
>     }
>
> -- stdout --
>
> Error 1: java.lang.RuntimeException: hello
> Error 2: java.util.concurrent.CompletionException:
> java.lang.RuntimeException: hello
> Exception in thread "main" java.util.concurrent.CompletionException:
> java.lang.RuntimeException: hello
>    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(
> CompletableFuture.java:331)
>    at java.base/java.util.concurrent.CompletableFuture.completeThrowable(
> CompletableFuture.java:346)
>    at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(
> CompletableFuture.java:870)
>    at java.base/java.util.concurrent.CompletableFuture.
> uniWhenCompleteStage(CompletableFuture.java:883)
>    at java.base/java.util.concurrent.CompletableFuture.whenComplete(
> CompletableFuture.java:2251)
>    at CF.main(CF.java:7)
> Caused by: java.lang.RuntimeException: hello
>    at CF.main(CF.java:6)
>
> -------------
>
> I guess the difference in these behaviors could be (somewhat) explained by
> this
> passage in the javadoc for CompletionStage:
>
>  * In all other cases, 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
>  * {@link CompletionException} holding the exception as its cause.
>
> What was the rationale behind passing a wrapped exception to the dependant
> stage? Why is it not wrapped in the first place (in the failedFuture
> method)?
>
> And finally. How would one organise exception handling not being sure if
> the
> target exception is wrapped or not?
>
> Thanks,
> -Pavel
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>



-- 
Best regards,
David Karnok
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20180117/ee63ace2/attachment.html>


More information about the Concurrency-interest mailing list