Exception handling with CompletionStage

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Exception handling with CompletionStage

JSR166 Concurrency mailing list
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
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Exception handling with CompletionStage

JSR166 Concurrency mailing list
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 <[hidden email]>:
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
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



--
Best regards,
David Karnok

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Exception handling with CompletionStage

JSR166 Concurrency mailing list
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 <[hidden email]> 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
> <[hidden email]>:
>>
>> 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
>> [hidden email]
>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>
>
>
>
> --
> Best regards,
> David Karnok
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest