Hi Martin,

*Unsurprisingly*, I think it is a bad idea to pollute something which was
created as a non-blocking superset intended to provide maximum utility with
minimum of sharp edges.

However, I think you have a point in that toCompletableFuture throwing UOE
is rather unhelpful, and if some people are huge fans of parking threads
then let's see if we can come to terms on a solution which doesn't
compromise the solution for everyone else.

The subject of this thread, "We need to add blocking methods to
CompletionStage!", gave me a few questions: "who is 'we'?", "when do we
need blocking methods?" and "Why is CompletionStage the right place to add
this?"

I think it's great that you point out that Scala's Future (full disclosure:
I am a co-designer of that) extends the Awaitable[1] trait (think
interface).
It is worth pointing out that those methods are not callable on the
instance directly (notice the implicit evidence type) so all invocations
need to go through an external construct, Await, which makes it abundantly
clear that something different is going to happen.

I wrote a long explanation of the design process (at least parts of it)
here[2], but I'm including the relevant section on Await below:

There’s an underlying question that I think is worth answering: “Why does
Await.result exist, and why doesn’t it exist *on* Future?”

When we designed Scala’s Future one thing we based it on was the experience
with Akka Future (
http://doc.akka.io/api/akka/1.3.1/?_ga=1.21043707.1579561034.1353497989#akka.dispatch.Future
)

Note how it had `get` and `await` methods, which do similar things as the
C# `Result` method — block the currently executing Thread from progressing
until the value is available. Such a method is the counterpart to
asynchronous, it is synchronizing the current Thread with the Thread which
executes the Future — i.e. an API for synchronous programming.

Not only did we find that methods like that introduce performance problems
due to blocking Threads (delay until the value is available but also due to
Thread scheduler wakeup lag), but these methods also produce programs that
are difficult to reason about since they can deadlock and become
non-deterministic in their runtime behavior as things like GC-pauses etc
may cause either spurious failures (where timeouts are supplied) or
prolonged resource starvation due to unavailability of Threads to execute
other logic.

Having a single method for performing these potentially dangerous
operations and putting it outside of the Future API itself meant that it is
easy to spot where blocking is performed as well as easy to outlaw it (by
disallowing it to be present in source code). But we also took it a step
further, by creating the BlockContext mechanism we also made it possible to
the runtime to perform evasive manoeuvres in the presence of blocking. (
http://www.scala-lang.org/api/current/index.html#scala.concurrent.BlockContext
)






*Option 1:*

Now, if Java's type system was just a tad more powerful, it would support
intersection types and APIs expressing the problem of wanting to expose
more than CompletionStage but less than the concrete CompletableFuture type
could very easily return `*CompletionStage[T] with Future[T]*`.

Now we don't really have that, so it needs to be poorly emulated using
compound interfaces, becoming something like `*FutureCompletionStage[T]*`
which would have the `join`-method on Future. And that may actually not be
a bad idea at all. So let's call that *Option 1.*



*Option 2:*

Add a `toFuture` method to CompletionStage such that if you want to go and
use the blocking methods of Future, it is both shorter and does not expose
a concrete type in the signature.

*Futher proposals:*

* Adding a constructor to `CompletableFuture` which takes a
`CompletionStage` as parameter would make the boilerplate discussed
earlier[3] obsolete.

* I'd like to see if we could make the `toCompletableFuture` be a default
method which delegates to `new CompletableFuture<>(this)`, meaning that the
UOE problem would sort of solvable.



*Summary:*

* I think there is already a great place to host blocking methods:
j.u.c.Future

* We can get around the UOE by introducing the constructor on
CompletableFuture both as a fallback to things which do throw UOE on
toCompletableFuture, but also provides a terrific default implementation
for toCompletableFuture.

* We can introduce a toFuture-method with a default implementation which
calls `new CompletableFuture<>(this)`


Have a great weekend!



*Footnotes:*
1:
http://www.scala-lang.org/files/archive/api/2.11.8/index.html#scala.concurrent.Awaitable
2: https://medium.com/@viktorklang/hi-eef4acf316a8#.uesy1fqgo
3: static <T> CompletableFuture<T> toCompletableFuture(CompletionStage<T>
stage) { … }


PS. As a sidenote, Martin, and in all friendliness, "actor purist API"?
C'mon, I know you're better than that! CompletionStage's design has nothing
to do with Actors and if Single Responsibility Principle is considered
purism then I'm not sure why we don't have a single interface with all
methods in it.
Let's try to keep things friendly.

PPS: A misunderstanding is that CompletionStage represents a running task
of sorts, one that can be cancelled etc. This is not the case.

PPPS: Adding blocking methods without mandatory timeouts has in practice
proven to be a recipe for disaster.

PPPPS: "I think it's unreasonable to not provide this for users (especially
when we can do so more efficiently)." <- If efficiency is desired then
blocking is definitely not the right solution.

PPPPPS: I'm currently moving cross-country so there will be delays of any
responses from me


On Wed, Sep 21, 2016 at 10:43 PM, Martin Buchholz <marti...@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.
>



-- 
Cheers,
√

Reply via email to