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