We need to add blocking methods to CompletionStage!

Viktor Klang viktor.klang at gmail.com
Mon Sep 26 14:55:17 UTC 2016

On Sun, Sep 25, 2016 at 11:49 PM, Martin Buchholz <martinrb at google.com>

> On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <viktor.klang at gmail.com>
> wrote:
>> 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.
> Let's be best friends!

One step at a time… :)

> The programming profession is still learning how to write concurrent
> programs, fumbling in the dark, and that's particularly true for myself!
> There are competing approaches and we don't know which will prove
> successful.

I'm not sure that they are all that competing TBH! There's always the
tradeoff between utility and safety. The problem is that the JDK is a
single layer for ALL Java programmers. Sometimes I think it would be wiser
to have one layer of high-utility/unsafer for library developers and other
"low-level tasks" and have a lower-utility/safer layer for more App-level
development. (I.e. always choose the least powerful thing with the highest
safety which gets the job done.)

> PPPS: Adding blocking methods without mandatory timeouts has in practice
>> proven to be a recipe for disaster.
> Hmmmm... By design, Unix processes that create subprocesses should
> dedicate a thread for each subprocess, that waits indefinitely in waitpid.
> Linux kernel folks deliberately decided this was fine.  In the Real World,
> one needs to interface with Other Peoples' APIs.

If you expect the machine to be up until the end of the universe, I think
it's fine to put an unbounded wait.
In the Real World—nothing waits forever for anything. (Evolution and all

> Software that rarely blocks is a great idea.  Especially the folks who
> write network server infrastructure increasingly agree.  But it is truly a
> difficult paradigm shift; we will get there slowly.

Trust me—having spent the past 6+ years on that journey—I know exactly what
you're talking about!

> ---
> Say you are implementing some existing function with a traditional
> synchronous API.  Your implementation is multi-threaded, but that's an
> implementation detail.  Before you return to the caller, you must wait for
> the various parts of your computation to complete (the "join" part of
> fork/join).  It seems reasonable to wait forever.  If some part of your
> computation is interacting with an unreliable external resource, then it
> makes sense for that particular "wait" (which need not occupy a thread) to
> have a timeout.

If you're implementing some existing method which expects a synchronous
response, but are doing so in a deferred way, that is exactly the kids of
situations where that tends to come back haunt you after a while.
Especially if calls to said implementation by some chance ends up being
executed on said multithreaded implementation's thread pool. (Enqueue
"hilarious" production deadlock-forever)

> Test methods,

Yeah, I thought so as well, but it turns out that when you have tons of
async tests, not being able to start new tests until either that timeout or
result makes for a very slow test suite, so that's why most serious test
frameworks are growing support for dealing with async code. Then all you
want to be able to limit is work-in-progress (sloppily called parallelism)

> main methods

That's a common scenario, but one that can be solved by having non-daemonic
pooled worker threads.

> and unix process reapers are all examples where it's reasonable to block
> forever.

What about waitpid() + WNOHANG?

>> 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.
> What about efficiently providing isComplete?

In my experience isComplete is virtually useless without being able to
extract the value, in which case you may as well introduce a non-blocking
`Optional<T> poll()`

> The result may already be available without actually blocking.  It may
> even be known to be available immediately.  One would like to get the value
> without additional allocation.

I've seen that use-case :), and it tends to either be a situation where the
value is available by pure luck (or…scheduling artifacts) or when one is
keeping CompletionStages where strict values could be kept instead (think
rebinding a value on completion).

Reading what your'e writing, may I dare propose that what you're after is
something along the lines of a: PollableCompletionStage which sits in
between CompletionStage and CompletableFuture?


More information about the core-libs-dev mailing list