We need to add blocking methods to CompletionStage!

Chris Hegarty chris.hegarty at oracle.com
Thu Sep 22 11:47:27 UTC 2016

Until now CS and CF have not appeared in Java SE API signatures,
outside of the j.u.c package. They are, however, currently being
proposed for use in API signatures for Java SE 9 [1][2], namely
j.l.Process[Handle]::onExit, and more extensively in the proposed new
HTTP Client.

CF was chosen, in some cases, mainly because it provides 'join'. While
some may not like it, a large number of developers still, at some point,
want to be able to block. It was felt that CF was more convenient,
rather than the CS::toCF dance.

In some cases CF provides too many knobs and is not quite suited. For
example, the java.net.http package description has the following
paragraph [3]:

 * <p> {@code CompletableFuture}s returned by this API will throw
 * {@link java.lang.UnsupportedOperationException} for their {@link
 * java.util.concurrent.CompletableFuture#obtrudeValue(Object) obtrudeValue}
 * and {@link java.util.concurrent.CompletableFuture#obtrudeException(Throwable)
 * obtrudeException} methods. Invoking the {@link
 * java.util.concurrent.CompletableFuture#cancel cancel} method on a
 * {@code CompletableFuture} returned by this API will not interrupt
 * the underlying operation, but may be useful to complete, exceptionally,
 * dependent stages that have not already completed.

At a minimum adding 'join' to CS would help ( and may cause the above
usages in JDK 9 to be revisited ). If this is not acceptable, or maybe
even separately, is there any appetite for a type between CS and CF,
that would expose a select set of methods ( which methods tbd ) that are
"more suited" [*] for use in the above kind of APIs, where the platform
or library is using them as a notification mechanism ( cancel may, or
may not, be useful to notify the platform / library that the operation /
result is no longer interesting, albeit somewhat of a hint ).


[1] http://download.java.net/java/jdk9/docs/api/java/util/concurrent/class-use/CompletableFuture.html
[2] http://download.java.net/java/jdk9/docs/api/java/util/concurrent/class-use/CompletionStage.html
[3] http://hg.openjdk.java.net/jdk9/sandbox/jdk/file/1fdd889687c8/src/java.httpclient/share/classes/java/net/http/package-info.java#l46
[*] for some definition of ...

> On 21 Sep 2016, at 21:43, Martin Buchholz <martinrb at google.com> wrote:
> (Sorry to re-open this discussion)
> The separation of a read-only CompletionStage from CompletableFuture is great.  I'm a fan of the scala style Promise/Future split as described in http://docs.scala-lang.org/overviews/core/futures.html, but: we need to re-add (safe, read-only) blocking methods like join.  Java is not Node.js, where there are no threads but there is a universal event loop.  Java programmers are used to Future, where the *only* way to use a future's value is to block waiting for it.  The existing CompletionStage methods are a better scaling alternative to blocking all the time, but blocking is almost always eventually necessary in Java.  For example, junit test methods that start any asynchronous computation need to block until the computation is done, before returning.
> As Viktor has pointed out, users can always implement blocking themselves by writing
>     static <T> CompletableFuture<T> toCompletableFuture(CompletionStage<T> stage) {
>         CompletableFuture<T> f = new CompletableFuture<>();
>         stage.handle((T t, Throwable ex) -> {
>                          if (ex != null) f.completeExceptionally(ex);
>                          else f.complete(t);
>                          return null;
>                      });
>         return f;
>     }
>     static <T> T join(CompletionStage<T> stage) {
>         return toCompletableFuture(stage).join();
>     }
> but unlike Viktor, I think it's unreasonable to not provide this for users (especially when we can do so more efficiently).  What is happening instead is API providers not using CompletionStage as return values in public APIs because of the lack of convenient blocking, and instead returning CompletableFuture, which is a tragic software engineering failure.
> Re-adding join is easy.  We discourage CompletionStage.toCompletableFuture from throwing UnsupportedOperationException, and implement join as:
>     public default T join() { return toCompletableFuture().join(); }
> There is a risk of multiple-inheritance conflict with Future if we add e.g. isDone(), but there are no current plans to turn those Future methods into default methods, and even if we did in some future release, it would be only a source, not binary incompatibility, so far less serious.

More information about the core-libs-dev mailing list