[concurrency-interest] Exception handling with CompletionStage

Pavel Rappo pavel.rappo at gmail.com
Wed Jan 17 16:35:37 EST 2018


Yeah, it seems like we have a couple of options here:

1) To unwrap the exception (like you did) by making a dependant stage which is
then completed manually

2) To wrap any directly passed exception into CompletionException

    CompletableFuture.failedFuture(
            new CompletionException(
                    new RuntimeException("hello")))

        or

    CompletableFuture<Object> cf = new CompletableFuture<>();
    cf.completeExceptionally(new CompletionException(new
RuntimeException("hello")));

Option (1) requires extra CompletionStage and the related boilerplate on the
producing site. Option (2) requires extra instanceof/getClass handling
boilerplate on each of the consuming sites.

Given this, I'll probably stick with option (1).

On Wed, Jan 17, 2018 at 8:47 PM, Dávid Karnok <akarnokd at gmail.com> wrote:
> 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


More information about the Concurrency-interest mailing list