More Javadoc problems

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

More Javadoc problems

cowwoc
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned stage exceptionally completes with this exception unless this stage also completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw exception2, which exception does the returned CompleteableFuture complete with? The specification says what *won't* happen if the stage also completed exceptionally, but it doesn't say what *will* happen. Please clarify :)

Thanks,
Gili
Reply | Threaded
Open this post in threaded view
|

Re: More Javadoc problems

cowwoc
cowwoc wrote
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned stage exceptionally completes with this exception unless this stage also completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw exception2, which exception does the returned CompleteableFuture complete with? The specification says what *won't* happen if the stage also completed exceptionally, but it doesn't say what *will* happen. Please clarify :)
It looks like in practice, exception1 is thrown and exception2 is lost.

Couldn't you at least add exception2 as a "suppressed" exception?

Gili
Reply | Threaded
Open this post in threaded view
|

Re: More Javadoc problems

Josh Humphries
In reply to this post by cowwoc
Looking at the source code -- the ultimate spec ;) -- the original exception is preferred. So in your example, the resulting CompletionStage completes exceptionally with exception1, and exception2 is effectively eaten.

----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  678-400-4867

On Fri, Dec 19, 2014 at 9:00 AM, cowwoc <[hidden email]> wrote:
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned
stage exceptionally completes with this exception unless this stage also
completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw
exception2, which exception does the returned CompleteableFuture complete
with? The specification says what *won't* happen if the stage also completed
exceptionally, but it doesn't say what *will* happen. Please clarify :)

Thanks,
Gili



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

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

Re: More Javadoc problems

cowwoc
Hi Josh,

Thanks for the follow-up.

I'd like to request two changes:
  1. Add exception2 as a suppressed exception (eating exceptions is like silent failures... it's bad business)
  2. Have the Javadoc to mention what will happen in this case.


Let me know what you think.

Thanks,
Gili

On 19/12/2014 9:42 AM, Josh Humphries [via JSR166 Concurrency] wrote:
Looking at the source code -- the ultimate spec ;) -- the original exception is preferred. So in your example, the resulting CompletionStage completes exceptionally with exception1, and exception2 is effectively eaten.

----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  678-400-4867

On Fri, Dec 19, 2014 at 9:00 AM, cowwoc <[hidden email]> wrote:
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned
stage exceptionally completes with this exception unless this stage also
completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw
exception2, which exception does the returned CompleteableFuture complete
with? The specification says what *won't* happen if the stage also completed
exceptionally, but it doesn't say what *will* happen. Please clarify :)

Thanks,
Gili



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



If you reply to this email, your message will be added to the discussion below:
http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669p11671.html
To unsubscribe from More Javadoc problems, click here.
NAML

Reply | Threaded
Open this post in threaded view
|

Re: More Javadoc problems

Josh Humphries
I do not disagree. Though one thing to note of this approach is that "observers" of the original completion stage (that failed with exception 1) will now witness side effects from a *successor* completion stage via the suppressed exception. It might be a little odd for some consumers to see the state of the Throwable change after it has already been (potentially) published to multiple threads.

I would personally vote that the later completion stage fail exceptionally with exception2. But that's a change in behavior that is likely to be a non-starter due to being incompatible with the current spec (even if only slightly).

The actual authors of the library are on this list and can likely chime in further about why it behaves that way.



----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  678-400-4867

On Fri, Dec 19, 2014 at 9:50 AM, cowwoc <[hidden email]> wrote:
Hi Josh,

Thanks for the follow-up.

I'd like to request two changes:
  1. Add exception2 as a suppressed exception (eating exceptions is like silent failures... it's bad business)
  2. Have the Javadoc to mention what will happen in this case.


Let me know what you think.

Thanks,
Gili

On 19/12/2014 9:42 AM, Josh Humphries [via JSR166 Concurrency] wrote:
Looking at the source code -- the ultimate spec ;) -- the original exception is preferred. So in your example, the resulting CompletionStage completes exceptionally with exception1, and exception2 is effectively eaten.

----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  <a href="tel:678-400-4867" value="+16784004867" target="_blank">678-400-4867

On Fri, Dec 19, 2014 at 9:00 AM, cowwoc <[hidden email]> wrote:
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned
stage exceptionally completes with this exception unless this stage also
completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw
exception2, which exception does the returned CompleteableFuture complete
with? The specification says what *won't* happen if the stage also completed
exceptionally, but it doesn't say what *will* happen. Please clarify :)

Thanks,
Gili



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



If you reply to this email, your message will be added to the discussion below:
http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669p11671.html
To unsubscribe from More Javadoc problems, click here.
NAML



View this message in context: Re: More Javadoc problems

Sent from the JSR166 Concurrency mailing list archive at Nabble.com.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


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

Re: More Javadoc problems

cowwoc
Hi Josh,

Can you give a concrete example where observers would witness side-effects?

Assuming what you say is possible, would you object to throwing a clone of the original exception instance with the suppressed exceptions added? This way only observers of the 2nd completion stage would observe these changes.

Gili

On 19/12/2014 10:40 AM, Josh Humphries wrote:
I do not disagree. Though one thing to note of this approach is that "observers" of the original completion stage (that failed with exception 1) will now witness side effects from a *successor* completion stage via the suppressed exception. It might be a little odd for some consumers to see the state of the Throwable change after it has already been (potentially) published to multiple threads.

I would personally vote that the later completion stage fail exceptionally with exception2. But that's a change in behavior that is likely to be a non-starter due to being incompatible with the current spec (even if only slightly).

The actual authors of the library are on this list and can likely chime in further about why it behaves that way.



----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  678-400-4867

On Fri, Dec 19, 2014 at 9:50 AM, cowwoc <[hidden email]> wrote:
Hi Josh,

Thanks for the follow-up.

I'd like to request two changes:
  1. Add exception2 as a suppressed exception (eating exceptions is like silent failures... it's bad business)
  2. Have the Javadoc to mention what will happen in this case.


Let me know what you think.

Thanks,
Gili

On 19/12/2014 9:42 AM, Josh Humphries [via JSR166 Concurrency] wrote:
Looking at the source code -- the ultimate spec ;) -- the original exception is preferred. So in your example, the resulting CompletionStage completes exceptionally with exception1, and exception2 is effectively eaten.

----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  <a moz-do-not-send="true" href="tel:678-400-4867" value="+16784004867" target="_blank">678-400-4867

On Fri, Dec 19, 2014 at 9:00 AM, cowwoc <[hidden email]> wrote:
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned
stage exceptionally completes with this exception unless this stage also
completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw
exception2, which exception does the returned CompleteableFuture complete
with? The specification says what *won't* happen if the stage also completed
exceptionally, but it doesn't say what *will* happen. Please clarify :)

Thanks,
Gili



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



If you reply to this email, your message will be added to the discussion below:
http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669p11671.html
To unsubscribe from More Javadoc problems, click here.
NAML



View this message in context: Re: More Javadoc problems

Sent from the JSR166 Concurrency mailing list archive at Nabble.com.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



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

Re: More Javadoc problems

Peter Levart

On 12/19/2014 05:45 PM, cowwoc wrote:
Hi Josh,

Can you give a concrete example where observers would witness side-effects?

Try this:


public class CFTest {

    static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) throws Exception {

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            sleep(200L);
            throw new RuntimeException("1st");
        });

        cf1.whenComplete((i, e1) -> {
            if (e1 != null) {
                sleep(100L);
                RuntimeException e2 = new RuntimeException("2nd");
                e1.addSuppressed(e2); // this is what is desired right?
                throw e2;
            }
        });

        try {
            cf1.join();
        } catch (CompletionException e) {
            System.out.println("\n1st print:\n");
            e.printStackTrace(System.out);
            sleep(200L);
            System.out.println("\n2nd print:\n");
            e.printStackTrace(System.out);
        }
    }
}


Assuming what you say is possible, would you object to throwing a clone of the original exception instance with the suppressed exceptions added? This way only observers of the 2nd completion stage would observe these changes.

Exceptions are not generally Cloneable.

Regards, Peter


Gili

On 19/12/2014 10:40 AM, Josh Humphries wrote:
I do not disagree. Though one thing to note of this approach is that "observers" of the original completion stage (that failed with exception 1) will now witness side effects from a *successor* completion stage via the suppressed exception. It might be a little odd for some consumers to see the state of the Throwable change after it has already been (potentially) published to multiple threads.

I would personally vote that the later completion stage fail exceptionally with exception2. But that's a change in behavior that is likely to be a non-starter due to being incompatible with the current spec (even if only slightly).

The actual authors of the library are on this list and can likely chime in further about why it behaves that way.



----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  678-400-4867

On Fri, Dec 19, 2014 at 9:50 AM, cowwoc <[hidden email]> wrote:
Hi Josh,

Thanks for the follow-up.

I'd like to request two changes:
  1. Add exception2 as a suppressed exception (eating exceptions is like silent failures... it's bad business)
  2. Have the Javadoc to mention what will happen in this case.


Let me know what you think.

Thanks,
Gili

On 19/12/2014 9:42 AM, Josh Humphries [via JSR166 Concurrency] wrote:
Looking at the source code -- the ultimate spec ;) -- the original exception is preferred. So in your example, the resulting CompletionStage completes exceptionally with exception1, and exception2 is effectively eaten.

----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  <a moz-do-not-send="true" href="tel:678-400-4867" value="+16784004867" target="_blank">678-400-4867

On Fri, Dec 19, 2014 at 9:00 AM, cowwoc <[hidden email]> wrote:
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned
stage exceptionally completes with this exception unless this stage also
completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw
exception2, which exception does the returned CompleteableFuture complete
with? The specification says what *won't* happen if the stage also completed
exceptionally, but it doesn't say what *will* happen. Please clarify :)

Thanks,
Gili



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



If you reply to this email, your message will be added to the discussion below:
http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669p11671.html
To unsubscribe from More Javadoc problems, click here.
NAML



View this message in context: Re: More Javadoc problems

Sent from the JSR166 Concurrency mailing list archive at Nabble.com.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


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

Re: More Javadoc problems

cowwoc
Hi Peter,

That's not what I meant. I meant that we should "throw e1" not "throw e2" near the end. I say this because I am under the impression that join() only fires after whenComplete() completes. Is that not the case?

Assuming I'm wrong, then I'd advocate an approach similar to what you said: throw a new exception (say e2) but instead of invoking e1.addSuppressed(e2) I'd do it the other way around: e2.addSuppressed(e1). This way main() would see e1 (unmodified) and anyone listening to the new stage would see e2.addSuppressed(e1).

The only problem (as you mentioned) is that the specification/Javadoc took a very undesirable path (throws e1 and ignores e2). I really hope we can change this.

Gili

On 19/12/2014 2:42 PM, Peter Levart wrote:

On 12/19/2014 05:45 PM, cowwoc wrote:
Hi Josh,

Can you give a concrete example where observers would witness side-effects?

Try this:


public class CFTest {

    static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) throws Exception {

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            sleep(200L);
            throw new RuntimeException("1st");
        });

        cf1.whenComplete((i, e1) -> {
            if (e1 != null) {
                sleep(100L);
                RuntimeException e2 = new RuntimeException("2nd");
                e1.addSuppressed(e2); // this is what is desired right?
                throw e2;
            }
        });

        try {
            cf1.join();
        } catch (CompletionException e) {
            System.out.println("\n1st print:\n");
            e.printStackTrace(System.out);
            sleep(200L);
            System.out.println("\n2nd print:\n");
            e.printStackTrace(System.out);
        }
    }
}


Assuming what you say is possible, would you object to throwing a clone of the original exception instance with the suppressed exceptions added? This way only observers of the 2nd completion stage would observe these changes.

Exceptions are not generally Cloneable.

Regards, Peter


Gili

On 19/12/2014 10:40 AM, Josh Humphries wrote:
I do not disagree. Though one thing to note of this approach is that "observers" of the original completion stage (that failed with exception 1) will now witness side effects from a *successor* completion stage via the suppressed exception. It might be a little odd for some consumers to see the state of the Throwable change after it has already been (potentially) published to multiple threads.

I would personally vote that the later completion stage fail exceptionally with exception2. But that's a change in behavior that is likely to be a non-starter due to being incompatible with the current spec (even if only slightly).

The actual authors of the library are on this list and can likely chime in further about why it behaves that way.



----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  678-400-4867

On Fri, Dec 19, 2014 at 9:50 AM, cowwoc <[hidden email]> wrote:
Hi Josh,

Thanks for the follow-up.

I'd like to request two changes:
  1. Add exception2 as a suppressed exception (eating exceptions is like silent failures... it's bad business)
  2. Have the Javadoc to mention what will happen in this case.


Let me know what you think.

Thanks,
Gili

On 19/12/2014 9:42 AM, Josh Humphries [via JSR166 Concurrency] wrote:
Looking at the source code -- the ultimate spec ;) -- the original exception is preferred. So in your example, the resulting CompletionStage completes exceptionally with exception1, and exception2 is effectively eaten.

----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  <a moz-do-not-send="true" href="tel:678-400-4867" value="+16784004867" target="_blank">678-400-4867

On Fri, Dec 19, 2014 at 9:00 AM, cowwoc <[hidden email]> wrote:
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned
stage exceptionally completes with this exception unless this stage also
completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw
exception2, which exception does the returned CompleteableFuture complete
with? The specification says what *won't* happen if the stage also completed
exceptionally, but it doesn't say what *will* happen. Please clarify :)

Thanks,
Gili



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



If you reply to this email, your message will be added to the discussion below:
http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669p11671.html
To unsubscribe from More Javadoc problems, click here.
NAML



View this message in context: Re: More Javadoc problems

Sent from the JSR166 Concurrency mailing list archive at Nabble.com.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



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

Re: More Javadoc problems

Peter Levart

On 12/19/2014 09:20 PM, cowwoc wrote:
Hi Peter,

That's not what I meant. I meant that we should "throw e1" not "throw e2" near the end. I say this because I am under the impression that join() only fires after whenComplete() completes. Is that not the case?

join() returns (or throws) when cf1 completes (cf1 has been asynchronously executing a Supplier in background thread - see supplyAsync). At the same time (but in another thread - the thread that just completed 'cf1') a whenComplete() BiConsumer is executed. So you have two threads (main thread and background thread) both accessing the same instance of exception 'e1' at the same time - the outcome of cf1. So you can't add 'e2' to the suppressed exceptions of 'e1' in the background thread without observing 'e1' being changed in main thread.


Assuming I'm wrong, then I'd advocate an approach similar to what you said: throw a new exception (say e2) but instead of invoking e1.addSuppressed(e2) I'd do it the other way around: e2.addSuppressed(e1). This way main() would see e1 (unmodified) and anyone listening to the new stage would see e2.addSuppressed(e1).

The only problem (as you mentioned) is that the specification/Javadoc took a very undesirable path (throws e1 and ignores e2). I really hope we can change this.

I think the intention of whenComplete is to provide a means of post-cleanup. Like implicit close in try-with-resources statement where the exception that gets propagated is the main exception from the body block. Since completion results can be consumed right-away, it is impossible to attach suppressed exceptions later when post-cleanup completes.

Why the 2nd stage completes with the same exception as 1st? I think because it is designed to complete with the same (exceptional OR nonexceptional) result as the 1st. If would be inconsistent to expect the same nonexceptional result but different exceptional.

If you want to change the exceptional OR nonexceptional outcome of preceeding stage, then use handle() instead of whenComplete().

Regards, Peter


Gili

On 19/12/2014 2:42 PM, Peter Levart wrote:

On 12/19/2014 05:45 PM, cowwoc wrote:
Hi Josh,

Can you give a concrete example where observers would witness side-effects?

Try this:


public class CFTest {

    static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) throws Exception {

        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            sleep(200L);
            throw new RuntimeException("1st");
        });

        cf1.whenComplete((i, e1) -> {
            if (e1 != null) {
                sleep(100L);
                RuntimeException e2 = new RuntimeException("2nd");
                e1.addSuppressed(e2); // this is what is desired right?
                throw e2;
            }
        });

        try {
            cf1.join();
        } catch (CompletionException e) {
            System.out.println("\n1st print:\n");
            e.printStackTrace(System.out);
            sleep(200L);
            System.out.println("\n2nd print:\n");
            e.printStackTrace(System.out);
        }
    }
}


Assuming what you say is possible, would you object to throwing a clone of the original exception instance with the suppressed exceptions added? This way only observers of the 2nd completion stage would observe these changes.

Exceptions are not generally Cloneable.

Regards, Peter


Gili

On 19/12/2014 10:40 AM, Josh Humphries wrote:
I do not disagree. Though one thing to note of this approach is that "observers" of the original completion stage (that failed with exception 1) will now witness side effects from a *successor* completion stage via the suppressed exception. It might be a little odd for some consumers to see the state of the Throwable change after it has already been (potentially) published to multiple threads.

I would personally vote that the later completion stage fail exceptionally with exception2. But that's a change in behavior that is likely to be a non-starter due to being incompatible with the current spec (even if only slightly).

The actual authors of the library are on this list and can likely chime in further about why it behaves that way.



----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  678-400-4867

On Fri, Dec 19, 2014 at 9:50 AM, cowwoc <[hidden email]> wrote:
Hi Josh,

Thanks for the follow-up.

I'd like to request two changes:
  1. Add exception2 as a suppressed exception (eating exceptions is like silent failures... it's bad business)
  2. Have the Javadoc to mention what will happen in this case.


Let me know what you think.

Thanks,
Gili

On 19/12/2014 9:42 AM, Josh Humphries [via JSR166 Concurrency] wrote:
Looking at the source code -- the ultimate spec ;) -- the original exception is preferred. So in your example, the resulting CompletionStage completes exceptionally with exception1, and exception2 is effectively eaten.

----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  <a moz-do-not-send="true" href="tel:678-400-4867" value="+16784004867" target="_blank">678-400-4867

On Fri, Dec 19, 2014 at 9:00 AM, cowwoc <[hidden email]> wrote:
Hi guys,

The Javadoc for CompletableFuture.whenComplete() reads:

"If the supplied action itself encounters an exception, then the returned
stage exceptionally completes with this exception unless this stage also
completed exceptionally."

Huh?!

So if the original stage completed with exception1 but the handler threw
exception2, which exception does the returned CompleteableFuture complete
with? The specification says what *won't* happen if the stage also completed
exceptionally, but it doesn't say what *will* happen. Please clarify :)

Thanks,
Gili



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



If you reply to this email, your message will be added to the discussion below:
http://jsr166-concurrency.10961.n7.nabble.com/More-Javadoc-problems-tp11669p11671.html
To unsubscribe from More Javadoc problems, click here.
NAML



View this message in context: Re: More Javadoc problems

Sent from the JSR166 Concurrency mailing list archive at Nabble.com.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




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

Re: More Javadoc problems

cowwoc
On 19/12/2014 4:10 PM, Peter Levart wrote:
If you want to change the exceptional OR nonexceptional outcome of preceeding stage, then use handle() instead of whenComplete().

I can't.

If you handle() and attempt to re-throw the same exception, you will get:

    unreported exception Throwable; must be caught or declared to be thrown

I agree that whenComplete() is meant to act as a finally block (which removes the need to do this funky casting). What I don't like is that join() returns without waiting for the result of whenComplete(). If the case of a real try-finally block, the code after the block does not execute after finally completes. I am trying to implement the same behavior here.

What should I be doing instead?

Gili

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

Re: More Javadoc problems

cowwoc
On 19/12/2014 4:30 PM, cowwoc wrote:
On 19/12/2014 4:10 PM, Peter Levart wrote:
If you want to change the exceptional OR nonexceptional outcome of preceeding stage, then use handle() instead of whenComplete().

I can't.

If you handle() and attempt to re-throw the same exception, you will get:

    unreported exception Throwable; must be caught or declared to be thrown

I agree that whenComplete() is meant to act as a finally block (which removes the need to do this funky casting). What I don't like is that join() returns without waiting for the result of whenComplete(). If the case of a real try-finally block, the code after the block does not execute after finally completes. I am trying to implement the same behavior here.

What should I be doing instead?

Actually, I think I'm fine.

I've got a function that returns "CompletableFuture.supplyAsync(lambda).whenComplete(lambda)" and I join() on that. So technically speaking, I believe this means I am joining against the second stage (after whenComplete() has run). Is that correct?

Gili

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

Re: More Javadoc problems

Gregg Wonderly-3
In reply to this post by Peter Levart
Of course, the typical solution is to throw some variant of RuntimeException to subvert the need to have all parts of the API declare the exception chain.  I like checked exceptions, and I really do wish that more people thought about how to document exceptional behavior in their APIs.   Many people seem to believe that there is only “one” way to use there code, and thus believe they have taken care of what needs to be taken care of.

These APIs are largely designed around “computational” activities which just throw RuntimeException for the most part.  The problem is, that “compute only” in the “internet age” is really a short sighted place to be.  All of the I/O necessities in today’s internet application development really requires an Exception path in all cases.  Developers will gripe and complain about it all day long, but once you really have developer, real apps, with I/O, you quickly learn that there are multiple places to deal with exceptions, and many times, at the very top of the call chain is where you need to “abort” the work in progress because that’s the only place that has the database transaction to cancel in view, the calling environment to report an error to, and all the other cleanup knowledge.

Gregg Wonderly

> On Dec 19, 2014, at 3:30 PM, cowwoc <[hidden email]> wrote:
>
> On 19/12/2014 4:10 PM, Peter Levart wrote:
>> If you want to change the exceptional OR nonexceptional outcome of preceeding stage, then use handle() instead of whenComplete().
>
> I can't.
>
> If you handle() and attempt to re-throw the same exception, you will get:
>
>     unreported exception Throwable; must be caught or declared to be thrown
>
> I agree that whenComplete() is meant to act as a finally block (which removes the need to do this funky casting). What I don't like is that join() returns without waiting for the result of whenComplete(). If the case of a real try-finally block, the code after the block does not execute after finally completes. I am trying to implement the same behavior here.
>
> What should I be doing instead?
>
> Gili
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest


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

Re: More Javadoc problems

Peter Levart
In reply to this post by cowwoc

On 12/19/2014 10:30 PM, cowwoc wrote:
On 19/12/2014 4:10 PM, Peter Levart wrote:
If you want to change the exceptional OR nonexceptional outcome of preceeding stage, then use handle() instead of whenComplete().

I can't.

If you handle() and attempt to re-throw the same exception, you will get:

    unreported exception Throwable; must be caught or declared to be thrown

This problem has been discussed before on the list. See here:

http://cs.oswego.edu/pipermail/concurrency-interest/2014-August/012907.html

The basic principle is to wrap the Throwable as a cause into a CompletionException.


I agree that whenComplete() is meant to act as a finally block (which removes the need to do this funky casting). What I don't like is that join() returns without waiting for the result of whenComplete(). If the case of a real try-finally block, the code after the block does not execute after finally completes. I am trying to implement the same behavior here.

What should I be doing instead?

Gili

If you join the 1st CompletableFuture (cf1), it returns when cf1 completes. The completion of cf1 triggers execution of the 2nd stage (cf2). If you join the cf2, it will return when cf2 (the whenComplete block) completes. Note that the program executes the stages in a background thread (requested with supplyAsync for the 1st stage). The same thread then executes following non-async stages. You are joining in main thread.

Regards, Peter

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

Re: More Javadoc problems

Peter Levart
In reply to this post by cowwoc

On 12/19/2014 10:39 PM, cowwoc wrote:
On 19/12/2014 4:30 PM, cowwoc wrote:
On 19/12/2014 4:10 PM, Peter Levart wrote:
If you want to change the exceptional OR nonexceptional outcome of preceeding stage, then use handle() instead of whenComplete().

I can't.

If you handle() and attempt to re-throw the same exception, you will get:

    unreported exception Throwable; must be caught or declared to be thrown

I agree that whenComplete() is meant to act as a finally block (which removes the need to do this funky casting). What I don't like is that join() returns without waiting for the result of whenComplete(). If the case of a real try-finally block, the code after the block does not execute after finally completes. I am trying to implement the same behavior here.

What should I be doing instead?

Actually, I think I'm fine.

I've got a function that returns "CompletableFuture.supplyAsync(lambda).whenComplete(lambda)" and I join() on that. So technically speaking, I believe this means I am joining against the second stage (after whenComplete() has run). Is that correct?

Gili

Correct.

Peter


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

Re: More Javadoc problems

cowwoc
In reply to this post by Peter Levart
On 19/12/2014 5:15 PM, Peter Levart wrote:

On 12/19/2014 10:30 PM, cowwoc wrote:
On 19/12/2014 4:10 PM, Peter Levart wrote:
If you want to change the exceptional OR nonexceptional outcome of preceeding stage, then use handle() instead of whenComplete().

I can't.

If you handle() and attempt to re-throw the same exception, you will get:

    unreported exception Throwable; must be caught or declared to be thrown

This problem has been discussed before on the list. See here:

http://cs.oswego.edu/pipermail/concurrency-interest/2014-August/012907.html

The basic principle is to wrap the Throwable as a cause into a CompletionException.

This is very useful, but why is this not documented in the Javadoc? It could be mentioned after (or as part of) the last bullet item in the class-level Javadoc.

I highly doubt anyone else will run into the aforementioned mailing list post. This information should be easier to find.

Thanks,
Gili

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

Re: More Javadoc problems

Doug Lea
In reply to this post by cowwoc
On 12/19/2014 09:00 AM, cowwoc wrote:
> The Javadoc for CompletableFuture.whenComplete() reads:
>
> "If the supplied action itself encounters an exception, then the returned
> stage exceptionally completes with this exception unless this stage also
> completed exceptionally."

First, thanks very much to Peter Levart for suggesting how to get
the effects you wanted.

I agree that this is terse, but I don't think it is ambiguous given
the previous sentence of the javadoc, which tells you what the
"unless" is referring to:

"Returns a new CompletionStage with the same result or exception as this stage,
that executes the given action when this stage completes."

The policy here is the same as in most other Java constructions.
The underlying notion is that a handler for one exception should
if at all possible locally arrange processing of any unrelated
exceptions it encounters. Otherwise there is no good way to
communicate them, so the usual policy is to preserve only the
outer "causal" exception. (See for example JLS "finally: specs
http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.2)
But when try-with-resources was introduced (which often hits
this problem), the Throwable.addSuppressed method was added to
provide a means of reporting them. As Peter pointed out, we
can't/shouldn't automate this, but it is usually a good idea.
On the other hand, as Peter noted, the "handle" method, that
allows arbitrary translations of results and/or exceptions, is
often a better fit in these cases.
(CompletionStage.whenComplete" is analogous to "finally",
CompletionStage.handle" is analogous to to "catch", and
CompletableFuture.exceptionally is a CompletableFuture-specific
simplification of "handle" that triggers only in the exception case,
otherwise preserving result.)

It might be helpful to develop a document discussing techniques
and suggested practices for dealing with exceptions in
CompletableFuture/CompletionStage designs.

-Doug

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

Re: More Javadoc problems

cowwoc
On 20/12/2014 7:19 AM, Doug Lea [via JSR166 Concurrency] wrote:

> On 12/19/2014 09:00 AM, cowwoc wrote:
> > The Javadoc for CompletableFuture.whenComplete() reads:
> >
> > "If the supplied action itself encounters an exception, then the
> returned
> > stage exceptionally completes with this exception unless this stage
> also
> > completed exceptionally."
>
> First, thanks very much to Peter Levart for suggesting how to get
> the effects you wanted.
>
> I agree that this is terse, but I don't think it is ambiguous given
> the previous sentence of the javadoc, which tells you what the
> "unless" is referring to:
>
> "Returns a new CompletionStage with the same result or exception as
> this stage,
> that executes the given action when this stage completes."

Doug, the "unless" and the paragraph you referred to are very distant
from one another. It's hard to draw the conclusion you mentioned unless
"unless" is one sentence away from what it is referring to. I suggest
changing:

     "If the supplied action itself encounters an exception, then the
returned stage exceptionally completes with this exception unless this
stage also completed exceptionally."

to:

     "If the supplied action itself encounters an exception, then the
returned stage exceptionally completes with this exception unless this
stage also completed exceptionally (in which case, the returned stage
exceptionally completes with the original exception)."

> The policy here is the same as in most other Java constructions.
> The underlying notion is that a handler for one exception should
> if at all possible locally arrange processing of any unrelated
> exceptions it encounters. Otherwise there is no good way to
> communicate them, so the usual policy is to preserve only the
> outer "causal" exception. (See for example JLS "finally: specs
> http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.2)
> But when try-with-resources was introduced (which often hits
> this problem), the Throwable.addSuppressed method was added to
> provide a means of reporting them. As Peter pointed out, we
> can't/shouldn't automate this, but it is usually a good idea.
> On the other hand, as Peter noted, the "handle" method, that
> allows arbitrary translations of results and/or exceptions, is
> often a better fit in these cases.
> (CompletionStage.whenComplete" is analogous to "finally",
> CompletionStage.handle" is analogous to to "catch", and
> CompletableFuture.exceptionally is a CompletableFuture-specific
> simplification of "handle" that triggers only in the exception case,
> otherwise preserving result.)
>
> It might be helpful to develop a document discussing techniques
> and suggested practices for dealing with exceptions in
> CompletableFuture/CompletionStage designs.
>
> -Doug

I would actually love it for whenComplete() to behave like finally()
because if it did it would throw exception2.addSuppressed(exception1)
instead of exception1. Would it not? I think this would be a lot less
confusing and would prevent the kind of information loss we are
currently seeing.

In any case, It would help if the Javadoc provided this information or
linked to a document that did. It would be great to read that
"whenComplete" is analogous to "finally", "handle" is analogous to
"catch" and so on. And also to read that one can use CompletionException
to wrap checked exceptions without having to do any manual unwrapping
later on.

PS: When is the online Javadoc updated? If you look at
http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#whenComplete-java.util.function.BiConsumer- 
you will notice that it doesn't contain any of this information. I'm
guessing that it corresponds to Java 8 update 0.

Thanks,
Gili
Reply | Threaded
Open this post in threaded view
|

Re: More Javadoc problems

cowwoc
In reply to this post by Doug Lea
On 20/12/2014 10:39 AM, cowwoc wrote:

> I would actually love it for whenComplete() to behave like finally()
> because if it did it would throw exception2.addSuppressed(exception1)
> instead of exception1. Would it not? I think this would be a lot less
> confusing and would prevent the kind of information loss we are
> currently seeing.
>
> In any case, It would help if the Javadoc provided this information or
> linked to a document that did. It would be great to read that
> "whenComplete" is analogous to "finally", "handle" is analogous to
> "catch" and so on. And also to read that one can use
> CompletionException to wrap checked exceptions without having to do
> any manual unwrapping later on.
>
> PS: When is the online Javadoc updated? If you look at
> http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#whenComplete-java.util.function.BiConsumer- 
> you will notice that it doesn't contain any of this information. I'm
> guessing that it corresponds to Java 8 update 0.
>
> Thanks,
> Gili

Now that I think about it: if the original Java8 specification did not
mandate what should happen if the action throws an exception, does that
mean we can change that specification now with minimal harm? Meaning, is
there a chance we can mandate exception2.addSuppressed(exception1) be
returned?

When was the Javadoc updated with the paragraph in question? Was it recent?

Gili
Reply | Threaded
Open this post in threaded view
|

Re: More Javadoc problems

Doug Lea
In reply to this post by cowwoc
On 12/20/2014 10:28 AM, cowwoc wrote:

> "unless" is one sentence away from what it is referring to. I suggest
> changing:
>
>       "If the supplied action itself encounters an exception, then the
> returned stage exceptionally completes with this exception unless this
> stage also completed exceptionally."
>
> to:
>
>       "If the supplied action itself encounters an exception, then the
> returned stage exceptionally completes with this exception unless this
> stage also completed exceptionally (in which case, the returned stage
> exceptionally completes with the original exception)."

Thanks. This should save other people some problems in decoding.
(This will probably take a while to propagate to OpenJDK.)

> I would actually love it for whenComplete() to behave like finally()

It does!

> because if it did it would throw exception2.addSuppressed(exception1)
> instead of exception1. Would it not?

Not. This is only done in try-with-resources.
See the (adjacent) JLS sections:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.2

http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3

I think that the best we can do here is to echo the standard strong
advice to localize handling of exceptions thrown in exception handlers,
and when this is impossible to use addSuppressed to alert downstream
completions of unrelated exceptions while handling the reported
exceptions. IDEs and tools might be helpful in spreading this advice.

> PS: When is the online Javadoc updated?

The only answer I know is "eventually" :-)

-Doug

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

Re: More Javadoc problems

cowwoc
On 20/12/2014 1:23 PM, Doug Lea [via JSR166 Concurrency] wrote:

> > because if it did it would throw exception2.addSuppressed(exception1)
> > instead of exception1. Would it not?
>
> Not. This is only done in try-with-resources.
> See the (adjacent) JLS sections:
>
> http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.2
>
> http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3
>

My interpretation of the first link is that:

try
{
   throw X;
}
finally
{
   throw Y;
}

will end up throwing Y. My interpretation of the second link is that:

try (resource)
{
   throw X;
}
finally
{
   throw Y;
}

will end up throwing X.addSuppressed(Y). But according to our
discussion, CompletableFuture ends up throwing X, which does not match
the behavior of try-finally or try-with-resources. Am I missing anything?

Thanks,
Gili