Re: Counter-intuitive behavior of CompleteableFuture

Tim Peierls via Concurrency-interest <[email protected]> Thu, 20 May 2021 16:21:31 -0400
Newsgroups gmane.comp.java.jsr.166-concurrency
Message-ID <CA+F8eeSS8-+XmeHmjzE+X4=0UwuMoFR_uSb8giwRDUC1mYZY6w@mail.gmail.com>
But orTimeout is also not available to Java 8 users.

Here’s a gist
<https://gist.github.com/Tembrel/c5762d1ba8ff4555a358aeada4bf5896> that
uses the Java 9 code to get the effect of orTimeout in Java 8. No
guarantees…

—tim

On Thu, May 20, 2021 at 4:15 PM Alex Otenko via Concurrency-interest <
[email protected]> wrote:

> It is a little more nuanced than that. You may not know that the CF is
> being completed on your behalf.
>
> Enter orTimeout...
>
> Furthermore, try to devise a chain of CFs that borrow a pooled resource,
> then finally return it back to the pool. Now if this construct is wrapped
> in orTimeout, you can see the resource borrowed, but downstream stages
> don't release it, because they will be bypassed by exceptional completion
> by orTimeout.
>
> Alex
>
> On Thu, 20 May 2021, 13:59 Viktor Klang via Concurrency-interest, <
> [email protected]> wrote:
>
>> 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,
>> √
>> _______________________________________________
>> 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
>

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