Quantcast

We need to add blocking methods to CompletionStage!

classic Classic list List threaded Threaded
48 messages Options
123
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

We need to add blocking methods to CompletionStage!

Martin Buchholz-3
(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.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Benjamin Manes
My limited understanding is that the original API was only CompletableFuture and that CompletionStage introduced as a compromise. It did not appear to be an attempt to strictly follow an interface-implementation separation, e.g. collections. As you said #toCompletableFuture() may throw an UOE, which means some use-cases can't rely on CompletionState which limits its usefulness. In my case that would be an AsyncLoadingCache with a synchronous LoadingCache view. I think having to code that the resulting implementation would be worse if it called toCompletableFuture, caught the exception, and then adapted as you said. 

When the new future class was introduced it was stated,

"In other words, we (j.u.c) are not now in a position to dictate a common interface for all SettableFuture, FutureValue, Promise, ListenableFuture, etc like APIs. And as we've seen, different audiences want/need different subsets of this API exposed as interfaces for their usages, and are in any case unlikely to want change all their existing interfaces. However, what we can do is provide a common underlying implementation that is as fast, scalable, space-conserving, carefully-specified, and reliable as possible. It should then be easy and attractive for others creating or reworking higher-level APIs to relay all functionality to the CompletableFuture implementation."  - Doug Lea, '12

I've gradually come to terms using CF as part of an API and haven't experienced a downside yet.

On Wed, Sep 21, 2016 at 1:43 PM, Martin Buchholz <[hidden email]> 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.

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



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Pavel Rappo
In reply to this post by Martin Buchholz-3
On Wed, Sep 21, 2016 at 9:43 PM, Martin Buchholz <[hidden email]> wrote:
> 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.

On Wed, Sep 21, 2016 at 10:25 PM, Benjamin Manes <[hidden email]> wrote:
> I've gradually come to terms using CF as part of an API and haven't
> experienced a downside yet.

I agree with Benjamin and would like to hear more about how it's a
"tragic software engineering failure".
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Martin Buchholz-3


On Wed, Sep 21, 2016 at 2:38 PM, Pavel Rappo <[hidden email]> wrote:
On Wed, Sep 21, 2016 at 9:43 PM, Martin Buchholz <[hidden email]> wrote:
> 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.

On Wed, Sep 21, 2016 at 10:25 PM, Benjamin Manes <[hidden email]> wrote:
> I've gradually come to terms using CF as part of an API and haven't
> experienced a downside yet.

I agree with Benjamin and would like to hear more about how it's a
"tragic software engineering failure".

Obviously I was exaggerating, but the software engineering benefits of separating the producer and consumer users into separate types seems big (like the Builder pattern for immutable collections).  Would you use CompletionStage if it had the other methods consumers need?

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Chris Hegarty
In reply to this post by Martin Buchholz-3
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 ).

-Chris.

[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 <[hidden email]> 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.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Martin Buchholz-3
In reply to this post by Martin Buchholz-3
Thanks for the lesson, James!

On Wed, Sep 21, 2016 at 3:57 PM, James Roper <[hidden email]> wrote:
On 22 September 2016 at 06:43, Martin Buchholz <[hidden email]> wrote:
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.
 
Out of interest, which APIs are returning CompletableFuture rather than CompletionStage?  In the Play Framework Java API, we have embraced CompletionStage, we use it absolutely everywhere.  Likewise in Lagom Framework and the Java API for Akka streams.

When it comes to blocking, we strongly advocate never blocking (in the threading models of these projects, blocking on a future will make you very prone to deadlocks).

I took a look at the Scala/Akka/LightBend world.  Even there, blocking always remains a possibility, even if discouraged, e.g. scala.concurrent.Future extends Awaitable (!), and 
And any general purpose Java API needs to be useful outside of a framework.
 
  But of course, the exception is junit tests, in that case, we encourage the use of CompletionStage.toCompletableFuture to block.

We're currently fixing jdk9 CompletableFuture.minimalCompletionStage().toCompletableFuture() to be awaitable. 

To make toCompletableFuture().join() more reliable as a recommended way to block, I think we should remove the encouragement to throw UOE from:

It sounds like the Akka/LightBend World is happy with current CompletionStage API (may I call this the "actor purist API"?), while other users will want a bigger, more general CompletableFuture subset for consuming future values.  Frustratingly, some of them will want cancel(), some not; and we have no good names for any new interfaces; right now all I can think of is CompletionStage2 and CompletionStage3 !)

The current implementation of CompletableFuture has a "memory leak problem" in that e.g. minimalStage().toCompletableFuture().isDone() will leak a Completion object until the source stage itself is collected.  Which doesn't happen if e.g. a direct isDone() is provided.
JDK-8161600: Garbage retention when source CompletableFutures are never completed
(but no one has ever complained!)

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Kasper Nielsen-4
In reply to this post by Martin Buchholz-3
On 21 September 2016 at 22:43, Martin Buchholz <[hidden email]> 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.  

Just want to say that I agree. I have using CS/CF for APIs extensively since Java 8. And all my usage basically boils down to 3 basic use cases. And I think most others will end up with the same use cases.

1) Observing
The user receives a CS where he can query about the state of the future and add follow up actions. I really would like to see the rest of the non-mutating methods from CompletableFuture added to CompletionStage here if possible.
get()
get(long, TimeUnit)
getNow()
isCompletedExceptionally
isDone()
isCompletedNormally() (isDone && !isCompletedExceptionally) <- new method

2) Cancellable
Tasks that can be cancelled by the user but where the user should not be able to modify the result or set a custom exception.
For example, cancel connecting to a remote host or cancel some internal computation.

Right not this is a bit painfull. I have to wrap CompletableFuture to allow cancel but hide complete/obtrude

Would not mind a CancellableCompletionStage interface
CancellableCompletionStage extends CompletionStage {
  isCancelled();
  cancel(boolean)
}

3) Mutable
The user has complete control. Basically just these three additional methods compared to CancellableCompletionStage.
  complete()
  completeExceptionally()
  obtrudeException()
I'm fine with returning CompletableFuture here.

Best
  Kasper




 

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Kasper Nielsen-4
Would not mind a CancellableCompletionStage interface
CancellableCompletionStage extends CompletionStage {
  isCancelled();
  cancel(boolean)
}


Just wanted to note. This is not just a question about functionality. It is also a signal to users
about "hey this a computation you can cancel". For example, if you where to retrofit all of 
java.nio you would probably use CancellableCompletionStage (or whatever the name) 
for all methods. You do not really need to set an int or whatever the read/write methods return 
in 99% of all cases.

Best
  Kasper



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Viktor Klang
In reply to this post by Martin Buchholz-3
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:


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 <[hidden email]> 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,

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Martin Buchholz-3
No one is suggesting we add cancel to CompletionStage - I agree that would break users, by making an immutable interface mutable.  This also means that CompletionStage cannot extend Future. I also would not want to have a toFuture method that would return a j.u.c.Future, because of misfit Future.cancel.  If we are adding cancel, then it will be to make a new interface, such as the suggested CancellableCompletionStage.

I also agree that CompletionStage does *not* represent "a running task of sorts", j.u.c. Future specs are still confusing in that way due to FutureTask heritage.

On Thu, Sep 22, 2016 at 7:51 PM, James Roper <[hidden email]> wrote:
For example, we often cache futures and return them from a libraries API, if a client could cancel a future, that would break everything else that received that future.

On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <[hidden email]> wrote:
PPS: A misunderstanding is that CompletionStage represents a running task of sorts, one that can be cancelled etc. This is not the case.

 

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Viktor Klang


On Sat, Sep 24, 2016 at 10:41 PM, Martin Buchholz <[hidden email]> wrote:
No one is suggesting we add cancel to CompletionStage - I agree that would break users, by making an immutable interface mutable. 

+10000
 
This also means that CompletionStage cannot extend Future.

+10000
 
I also would not want to have a toFuture method that would return a j.u.c.Future, because of misfit Future.cancel.

Would you mind elaborating here? According to the cancel method spec on Future it is completely fine for it to be a no-op which always returns false:

"This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason."


 
  If we are adding cancel, then it will be to make a new interface, such as the suggested CancellableCompletionStage.

I also agree that CompletionStage does *not* represent "a running task of sorts", j.u.c. Future specs are still confusing in that way due to FutureTask heritage.

+10000
 

On Thu, Sep 22, 2016 at 7:51 PM, James Roper <[hidden email]> wrote:
For example, we often cache futures and return them from a libraries API, if a client could cancel a future, that would break everything else that received that future.

On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <[hidden email]> wrote:
PPS: A misunderstanding is that CompletionStage represents a running task of sorts, one that can be cancelled etc. This is not the case.

 



--
Cheers,

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Viktor Klang


On Sun, Sep 25, 2016 at 12:34 PM, Viktor Klang <[hidden email]> wrote:


On Sat, Sep 24, 2016 at 10:41 PM, Martin Buchholz <[hidden email]> wrote:
No one is suggesting we add cancel to CompletionStage - I agree that would break users, by making an immutable interface mutable. 

+10000
 
This also means that CompletionStage cannot extend Future.

+10000
 
I also would not want to have a toFuture method that would return a j.u.c.Future, because of misfit Future.cancel.

Would you mind elaborating here? According to the cancel method spec on Future it is completely fine for it to be a no-op which always returns false:

"This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason."


There's an interesting (read: weird) spec clause in cancel:

"After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true."
 
That clause is in contradiction with the previously quoted line.



 
  If we are adding cancel, then it will be to make a new interface, such as the suggested CancellableCompletionStage.

I also agree that CompletionStage does *not* represent "a running task of sorts", j.u.c. Future specs are still confusing in that way due to FutureTask heritage.

+10000
 

On Thu, Sep 22, 2016 at 7:51 PM, James Roper <[hidden email]> wrote:
For example, we often cache futures and return them from a libraries API, if a client could cancel a future, that would break everything else that received that future.

On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <[hidden email]> wrote:
PPS: A misunderstanding is that CompletionStage represents a running task of sorts, one that can be cancelled etc. This is not the case.

 



--
Cheers,



--
Cheers,

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

David Holmes-6

I think that was meant to read “After this method returns _true_, subsequent calls …”

 

David

 

From: Concurrency-interest [mailto:[hidden email]] On Behalf Of Viktor Klang
Sent: Sunday, September 25, 2016 9:03 PM
To: Martin Buchholz <[hidden email]>
Cc: concurrency-interest <[hidden email]>; core-libs-dev <[hidden email]>
Subject: Re: [concurrency-interest] We need to add blocking methods to CompletionStage!

 

 

 

On Sun, Sep 25, 2016 at 12:34 PM, Viktor Klang <[hidden email]> wrote:

 

 

On Sat, Sep 24, 2016 at 10:41 PM, Martin Buchholz <[hidden email]> wrote:

No one is suggesting we add cancel to CompletionStage - I agree that would break users, by making an immutable interface mutable. 

 

+10000

 

This also means that CompletionStage cannot extend Future.

 

+10000

 

I also would not want to have a toFuture method that would return a j.u.c.Future, because of misfit Future.cancel.

 

Would you mind elaborating here? According to the cancel method spec on Future it is completely fine for it to be a no-op which always returns false:

 

"This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason."

 

 

There's an interesting (read: weird) spec clause in cancel:

 

"After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true."

 

That clause is in contradiction with the previously quoted line.

 

 

 

 

  If we are adding cancel, then it will be to make a new interface, such as the suggested CancellableCompletionStage.

 

I also agree that CompletionStage does *not* represent "a running task of sorts", j.u.c. Future specs are still confusing in that way due to FutureTask heritage.

 

+10000

 

 

On Thu, Sep 22, 2016 at 7:51 PM, James Roper <[hidden email]> wrote:

For example, we often cache futures and return them from a libraries API, if a client could cancel a future, that would break everything else that received that future.

 

On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <[hidden email]> wrote:

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

 

 



 

--

Cheers,



 

--

Cheers,


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Doug Lea
In reply to this post by Martin Buchholz-3

Thanks to Martin for trying to clarify disagreements about and
possible improvements to CompletableFuture and CompletionStage APIs.
A few notes:

The main issue continues to be: what would you like clients to be able
to do with a CompletableFuture returned to them.  This is reminiscent
of Collections APIs: is it OK for clients to add or remove items in a
collection passed or returned to them? In some cases, the consequences
of nonsensical modifications are no different in scope than other
usage errors, so the issue amounts to API usage guidance.  But
sometimes you need to prevent modifications. In which case, similar
answers apply for collections and completions (although it is not a
perfect analogy because you cannot sidestep issues using only pure
ImmutableCompletions -- someone needs to complete them).

1.  Producers can let clients do anything if the clients only modify a
copy. Unlike some Collections, "defensive copying" CompletableFutures
is fairly cheap, so is usually a good solution. Similarly, clients
should be able to easily make a copy themselves if they are in doubt
about for example upstream effects of calling cancel(). We now know
that copy() should have been in jdk8 (as it now is in jdk9) rather
than forcing people to do this via thenApply(x->x), which made the
defensive copying approach underused.

2. Alternatively, producers can return a type that does not allow some
operations.  Method minimalCompletionStage is analogous to Collections
unmodifiableCollection, but to some people, too minimal. Even joining
and status checks can lead to deadlocks and livelocks that some
producers want to make impossible to encounter without explicit client
action. Introducing only CompletionStage/minimalCompletionStage might
not have been the best approach, because further extending this
strategy leads to further controversy choosing among the many
plausible intermediary points of sets of methods to expose between
CompletionStage and CompletableFuture. But if we were to pick one, we
might consider something along the lines suggested by Kasper and
Viktor of
   interface FutureCompletion extends CompletionStage, Future {}
This allows joins and queries, but also raises the policy question of
whether cancel() should only cancel a copy or the source.


Separately, we also now know that it was uselessly mean-spirited to
allow CompletionStage.toCompletableFuture to throw UOE, since any
client can awkwardly emulate this using whenComplete.  I'm not sure
whether was can compatibly revise to disallow UOE and also
default-implement it.

-Doug

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

joe.bowbeer
In reply to this post by David Holmes-6

This statement regarding what happens after cancel is called is correct:

"After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true."

After cancel returns, the future is completed, hence isDone. If cancel returns true, i.e. it was cancelled, then  isCancelled returns true. But, for example if the future is already completed when cancel is called, then cancel will return false and isCancelled will return false.


On Sep 25, 2016 6:49 AM, "David Holmes" <[hidden email]> wrote:

I think that was meant to read “After this method returns _true_, subsequent calls …”

 

David

 

From: Concurrency-interest [mailto:[hidden email]] On Behalf Of Viktor Klang
Sent: Sunday, September 25, 2016 9:03 PM
To: Martin Buchholz <[hidden email]>
Cc: concurrency-interest <[hidden email]>; core-libs-dev <[hidden email]>
Subject: Re: [concurrency-interest] We need to add blocking methods to CompletionStage!

 

 

 

On Sun, Sep 25, 2016 at 12:34 PM, Viktor Klang <[hidden email]> wrote:

 

 

On Sat, Sep 24, 2016 at 10:41 PM, Martin Buchholz <[hidden email]> wrote:

No one is suggesting we add cancel to CompletionStage - I agree that would break users, by making an immutable interface mutable. 

 

+10000

 

This also means that CompletionStage cannot extend Future.

 

+10000

 

I also would not want to have a toFuture method that would return a j.u.c.Future, because of misfit Future.cancel.

 

Would you mind elaborating here? According to the cancel method spec on Future it is completely fine for it to be a no-op which always returns false:

 

"This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason."

 

 

There's an interesting (read: weird) spec clause in cancel:

 

"After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true."

 

That clause is in contradiction with the previously quoted line.

 

 

 

 

  If we are adding cancel, then it will be to make a new interface, such as the suggested CancellableCompletionStage.

 

I also agree that CompletionStage does *not* represent "a running task of sorts", j.u.c. Future specs are still confusing in that way due to FutureTask heritage.

 

+10000

 

 

On Thu, Sep 22, 2016 at 7:51 PM, James Roper <[hidden email]> wrote:

For example, we often cache futures and return them from a libraries API, if a client could cancel a future, that would break everything else that received that future.

 

On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <[hidden email]> wrote:

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

 

 



 

--

Cheers,



 

--

Cheers,


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


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Viktor Klang

If that truely is the case then the only way of implementing a readonly Future is by throwing an exception from cancel...

--
Cheers,


On Sep 25, 2016 4:20 PM, "Joe Bowbeer" <[hidden email]> wrote:

This statement regarding what happens after cancel is called is correct:

"After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true."

After cancel returns, the future is completed, hence isDone. If cancel returns true, i.e. it was cancelled, then  isCancelled returns true. But, for example if the future is already completed when cancel is called, then cancel will return false and isCancelled will return false.


On Sep 25, 2016 6:49 AM, "David Holmes" <[hidden email]> wrote:

I think that was meant to read “After this method returns _true_, subsequent calls …”

 

David

 

From: Concurrency-interest [mailto:[hidden email]] On Behalf Of Viktor Klang
Sent: Sunday, September 25, 2016 9:03 PM
To: Martin Buchholz <[hidden email]>
Cc: concurrency-interest <[hidden email]>; core-libs-dev <[hidden email]>
Subject: Re: [concurrency-interest] We need to add blocking methods to CompletionStage!

 

 

 

On Sun, Sep 25, 2016 at 12:34 PM, Viktor Klang <[hidden email]> wrote:

 

 

On Sat, Sep 24, 2016 at 10:41 PM, Martin Buchholz <[hidden email]> wrote:

No one is suggesting we add cancel to CompletionStage - I agree that would break users, by making an immutable interface mutable. 

 

+10000

 

This also means that CompletionStage cannot extend Future.

 

+10000

 

I also would not want to have a toFuture method that would return a j.u.c.Future, because of misfit Future.cancel.

 

Would you mind elaborating here? According to the cancel method spec on Future it is completely fine for it to be a no-op which always returns false:

 

"This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason."

 

 

There's an interesting (read: weird) spec clause in cancel:

 

"After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true."

 

That clause is in contradiction with the previously quoted line.

 

 

 

 

  If we are adding cancel, then it will be to make a new interface, such as the suggested CancellableCompletionStage.

 

I also agree that CompletionStage does *not* represent "a running task of sorts", j.u.c. Future specs are still confusing in that way due to FutureTask heritage.

 

+10000

 

 

On Thu, Sep 22, 2016 at 7:51 PM, James Roper <[hidden email]> wrote:

For example, we often cache futures and return them from a libraries API, if a client could cancel a future, that would break everything else that received that future.

 

On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <[hidden email]> wrote:

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

 

 



 

--

Cheers,



 

--

Cheers,


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


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Martin Buchholz-3


On Sun, Sep 25, 2016 at 7:34 AM, Viktor Klang <[hidden email]> wrote:

If that truely is the case then the only way of implementing a readonly Future is by throwing an exception from cancel...

We the maintainers of j.u.c.Future have always thought that canceling a Future will surely leave it completed.  Of course, implementers of any Java interface can choose to throw UOE for any method, but this is not intended for Future.cancel.  An interface without cancel probably should not be extending Future.

---

Here's another way to think of the range of functionality between current CompletionStage and current CompletableFuture:

- Add Polling methods from scala Future such as isCompleted
These are also discouraged, but less so than Await methods
"""Note: using this method yields nondeterministic dataflow programs."""
Will adding these to CompletionStage be less controversial?

- Add Await blocking methods (that in scala cannot be called directly, using the CanAwait mechanism)

- Add cancellation

- Add other methods to complete the future ("Promise")

- Add the full CompletableFuture API, including the obtrude methods

Cancellation is interesting, because it's a mutating method, and so cannot be used with a future shared with other users, but it also seems very reasonable as a "client" operation.  One can think of obtaining a non-shared future as a subscription to a one-time event, and that subscription takes up resources in the form of an object.  If the client is no longer interested in the event, then cancellation of the future can free up resources.  I'm still thinking of what Process.onExit should return.  There's a tension between the various roles that CompletableFuture serves - as a container to write a value, as a possibly cancellable subscription to that value, and as a way to chain async computations together.

On Wed, Sep 21, 2016 at 2:25 PM, Benjamin Manes <[hidden email]> wrote:
I've gradually come to terms using CF as part of an API and haven't experienced a downside yet.

Hmmmm.... I seem to be moving in that direction as well...  

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

David Holmes-6
In reply to this post by joe.bowbeer

Joe,

 

That is ignoring the error case. If the cancel fails then it is not complete and it is not cancelled. We added the extra wording back in August 2005. It is interesting to note that Martin’s initial query then only related to the state of the thread, but that it was clear about things only happening if cancel returned true:

 

My guess is that if cancel(true) returns true, then subsequent cals to isCancelled() and isDone() will return true, *even if the thread executing the task is still running*

 

Yet we somehow added the clarification with no regard as to whether cancel returned true or not. That seems wrong.

 

David

 

From: Concurrency-interest [mailto:[hidden email]] On Behalf Of Joe Bowbeer
Sent: Monday, September 26, 2016 12:20 AM
To: David Holmes <[hidden email]>
Cc: Martin Buchholz <[hidden email]>; concurrency-interest <[hidden email]>; core-libs-dev <[hidden email]>
Subject: Re: [concurrency-interest] We need to add blocking methods to CompletionStage!

 

This statement regarding what happens after cancel is called is correct:

"After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true."

After cancel returns, the future is completed, hence isDone. If cancel returns true, i.e. it was cancelled, then  isCancelled returns true. But, for example if the future is already completed when cancel is called, then cancel will return false and isCancelled will return false.

 

On Sep 25, 2016 6:49 AM, "David Holmes" <[hidden email]> wrote:

I think that was meant to read “After this method returns _true_, subsequent calls …”

 

David

 

From: Concurrency-interest [mailto:[hidden email]] On Behalf Of Viktor Klang
Sent: Sunday, September 25, 2016 9:03 PM
To: Martin Buchholz <[hidden email]>
Cc: concurrency-interest <[hidden email]>; core-libs-dev <[hidden email]>
Subject: Re: [concurrency-interest] We need to add blocking methods to CompletionStage!

 

 

 

On Sun, Sep 25, 2016 at 12:34 PM, Viktor Klang <[hidden email]> wrote:

 

 

On Sat, Sep 24, 2016 at 10:41 PM, Martin Buchholz <[hidden email]> wrote:

No one is suggesting we add cancel to CompletionStage - I agree that would break users, by making an immutable interface mutable. 

 

+10000

 

This also means that CompletionStage cannot extend Future.

 

+10000

 

I also would not want to have a toFuture method that would return a j.u.c.Future, because of misfit Future.cancel.

 

Would you mind elaborating here? According to the cancel method spec on Future it is completely fine for it to be a no-op which always returns false:

 

"This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason."

 

 

There's an interesting (read: weird) spec clause in cancel:

 

"After this method returns, subsequent calls to isDone() will always return true. Subsequent calls to isCancelled() will always return true if this method returned true."

 

That clause is in contradiction with the previously quoted line.

 

 

 

 

  If we are adding cancel, then it will be to make a new interface, such as the suggested CancellableCompletionStage.

 

I also agree that CompletionStage does *not* represent "a running task of sorts", j.u.c. Future specs are still confusing in that way due to FutureTask heritage.

 

+10000

 

 

On Thu, Sep 22, 2016 at 7:51 PM, James Roper <[hidden email]> wrote:

For example, we often cache futures and return them from a libraries API, if a client could cancel a future, that would break everything else that received that future.

 

On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <[hidden email]> wrote:

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

 

 



 

--

Cheers,



 

--

Cheers,


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


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Martin Buchholz-3
In reply to this post by Viktor Klang


On Fri, Sep 23, 2016 at 2:24 PM, Viktor Klang <[hidden email]> 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!

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.

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.

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.

---

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.  Test methods, main methods and unix process reapers are all examples where it's reasonable to block forever.
 
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?

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. 

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: We need to add blocking methods to CompletionStage!

Alex Otenko

> On 25 Sep 2016, at 22:49, Martin Buchholz <[hidden email]> wrote:
> ...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.  Test methods, main methods and unix process reapers are all examples where it's reasonable to block forever.

More than that, cancelling the wait does not cancel the computation it was waiting for.

You need to time out / cancel the wait at the junctions where you have the power to arbitrate fair use of resource locked up in the caller, by the caller.

Alex
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
123
Loading...