Re: Counter-intuitive behavior of CompleteableFuture

Michał Górniewski via Concurrency-interest <[email protected]> Thu, 20 May 2021 15:28:29 +0200
Newsgroups gmane.comp.java.jsr.166-concurrency
Message-ID <CAMFpyQKPunJyRPCbQrNmRp0utx0gy=sYnNdHKNtFg61BpFHy7g@mail.gmail.com>
I'm still on Java 8, but this is a very good solution :)

Thanks,
Michał

czw., 20 maj 2021 o 15:22 Viktor Klang <[email protected]> napisał(a):
>
> minimalCompletionStage to the rescue :)
>
>
> public static void main(String[] args) {
>     CompletableFuture<String> initial = new CompletableFuture<>();
>
>     CompletionStage<String> withSteps = initial
>             .minimalCompletionStage                                   <--- This will prevent consumers from having toCompletableFuture returning `this`
>             .thenApply(s -> {
>                 System.out.println("STEP1: " + s);
>                 return s;
>             })
>             .thenApply(s -> {
>                 System.out.println("STEP2: " + s);
>                 return s;
>             });
>
>     CompletionStage<String> timeout = withSteps;
>
>     timeout.whenComplete((s, throwable) ->
> System.out.println("TIMEOUT: " + throwable));
>
>     timeout.completeExceptionally(new RuntimeException("TIMEOUT")); <--- now won't compile
>     initial.complete("SUCCESS");
> }
>
> On Thu, May 20, 2021 at 1:14 PM Michał Górniewski <[email protected]> wrote:
>>
>> This looks good from an API design perspective, since it clearly
>> informs users that it really shouldn't call complete() on this Future
>> :).
>>
>> But still, in Java you can do:
>>
>> timeout.toCompletableFuture().completeExceptionally(new
>> RuntimeException("TIMEOUT"));
>>
>> So toCompletableFuture() would need to create new, depended,
>> CompletableStage to be safe.
>>
>> Best regards,
>> Michał
>>
>> czw., 20 maj 2021 o 14:59 Viktor Klang <[email protected]> napisał(a):
>> >
>> > A solution which has been used very successfully in Scala is the distinction between Promise (write a single value 0..1 times) and Future (read a single value 0..N times), which means that only the thread who owns a reference to the Promise can write values, which means that Futures can be freely shared since consumers cannot manipulate the result "further up the chain".
>> >
>> > You can achieve something similar in Java by separating CompletableFuture from CompletionStage.
>> >
>> > public static void main(String[] args) {
>> >     CompletableFuture<String> initial = new CompletableFuture<>();
>> >
>> >     CompletionStage<String> withSteps = initial
>> >             .thenApply(s -> {
>> >                 System.out.println("STEP1: " + s);
>> >                 return s;
>> >             })
>> >             .thenApply(s -> {
>> >                 System.out.println("STEP2: " + s);
>> >                 return s;
>> >             });
>> >
>> >     CompletionStage<String> timeout = withSteps;
>> >
>> >     timeout.whenComplete((s, throwable) ->
>> > System.out.println("TIMEOUT: " + throwable));
>> >
>> >     timeout.completeExceptionally(new RuntimeException("TIMEOUT")); <--- now won't compile
>> >     initial.complete("SUCCESS");
>> > }
>> >
>> >
>> > On Thu, May 20, 2021 at 12:50 PM Michał Górniewski via Concurrency-interest <[email protected]> wrote:
>> >>
>> >> What bothers me here is API of CompletableFuture itself, e.g:
>> >>
>> >> public CompletableFuture<ProcessingOutput>
>> >> executeProcessing(Function<Path, ProcessingOutput> processor) {
>> >>     Path data = prepareData();
>> >>     return CompletableFuture.supplyAsync(() -> processor.apply(data),
>> >> processingExecutor)
>> >>             .whenComplete((o, ex) -> cleanup(data));
>> >> }
>> >>
>> >> Callers of this method may just break cleanup, without even knowing
>> >> about this, but simply calling complete() on returned CF. But it seems
>> >> that nothing can be done about it.
>> >>
>> >> czw., 20 maj 2021 o 14:40 Benjamin Manes <[email protected]> napisał(a):
>> >> >
>> >> > The timeout is applied to the STEP2 future which completes it exceptionally. This causes it to not run the mapping function for a result, as it was completed prior to that being triggered. You can use copy() to decouple the source from the timeout if you want both to complete; e.g. cache a long running value but fail a consumer if taking to long.
>> >> >
>> >> > On Thu, May 20, 2021 at 5:31 AM Michał Górniewski via Concurrency-interest <[email protected]> wrote:
>> >> >>
>> >> >> With following code:
>> >> >>
>> >> >> ------------------------------
>> >> >> public static void main(String[] args) {
>> >> >>     CompletableFuture<String> initial = new CompletableFuture<>();
>> >> >>
>> >> >>     CompletableFuture<String> withSteps = initial
>> >> >>             .thenApply(s -> {
>> >> >>                 System.out.println("STEP1: " + s);
>> >> >>                 return s;
>> >> >>             })
>> >> >>             .thenApply(s -> {
>> >> >>                 System.out.println("STEP2: " + s);
>> >> >>                 return s;
>> >> >>             });
>> >> >>
>> >> >>     CompletableFuture<String> timeout = withSteps;
>> >> >>
>> >> >>     timeout.whenComplete((s, throwable) ->
>> >> >> System.out.println("TIMEOUT: " + throwable));
>> >> >>
>> >> >>     timeout.completeExceptionally(new RuntimeException("TIMEOUT"));
>> >> >>     initial.complete("SUCCESS");
>> >> >> }
>> >> >> ------------------------------
>> >> >>
>> >> >> I got output like this:
>> >> >>
>> >> >> ------------------------------
>> >> >> TIMEOUT: java.lang.RuntimeException: TIMEOUT
>> >> >> STEP1: SUCCESS
>> >> >> ------------------------------
>> >> >>
>> >> >> I don't understand why "STEP2" is not executed in this case. Is this expected?
>> >> >> For me this API is confusing and may create issues like this:
>> >> >> https://github.com/resilience4j/resilience4j/issues/1427
>> >> >>
>> >> >> Thanks,
>> >> >> Michał Górniewski
>> >> >> _______________________________________________
>> >> >> Concurrency-interest mailing list
>> >> >> [email protected]
>> >> >> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>> >> _______________________________________________
>> >> Concurrency-interest mailing list
>> >> [email protected]
>> >> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>> >
>> >
>> >
>> > --
>> > Cheers,
>> > √
>
>
>
> --
> Cheers,
> √
_______________________________________________
Concurrency-interest mailing list
[email protected]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest