CompletionStage

Sam Pullara sam at sampullara.com
Sun Jun 30 17:24:33 PDT 2013


On Sun, Jun 30, 2013 at 4:02 PM, Doug Lea <dl at cs.oswego.edu> wrote:

>
>>
>> Yeah, you guys keep claiming that, while I see people with pretty general
>> solutions getting by just fine — for example, not having
>> takeWhile/takeUntil on
>> Stream is a real black eye on the API.
>>
>>
> The base issues are pretty straightforward, which does not
> make them them straightforwardly solvable:
>
> * Active cancellation requires polling of non-local atomically
> managed shared state.
>
> * Choosing where and how often to poll is a function of the
> responsiveness vs throughput vs resource requirements of users.
>
> * No choice satisfies everyone. In particular, applications that do
> not require active cancellation are slowed down proportionally to
> how often this state is needlessly polled.
>
> * Hence, the optimal solution in terms of expected quality of
> service for a library framework is to force users to do the polling
> themselves in their own code.
>
> * Unfortunately, most users hate to do this.
>
> Hard to win.
>

I'm not sure where active polling comes in. I just want a callback for each
CompletionFuture when a cancellation at the top level is requested. Whether
my code uses polling to cancel or some other mechanism is up to the
receiver of the message. The change above may be sufficient, just a little
odd that cancellation and completion are being mixed together.

Experimenting a bit with the API to see if I could use it for cancellation,
here is a program that I would like to work much differently than it does
with the current system:

    @Test
    public void testCancellation() throws ExecutionException,
InterruptedException {
        AtomicBoolean cancelled = new AtomicBoolean();
        AtomicBoolean handled = new AtomicBoolean();
        AtomicBoolean handleCalledWithValue = new AtomicBoolean();
        CompletableFuture<String> other = supplyAsync(() -> "Doomed value");
        CompletableFuture<String> future = supplyAsync(() -> {
            sleep(1000);
            return "Doomed value";
        }).exceptionally(t -> {
            cancelled.set(true);
            return null;
        }).thenCombine(other, (a, b) -> a + ", " + b).handle((v, t) -> {
            if (t == null) {
                handleCalledWithValue.set(true);
            }
            handled.set(true);
            return null;
        });
        sleep(100);
        future.cancel(true);
        sleep(1000);
        try {
            future.get();
            fail("Should have thrown");
        } catch (CancellationException ce) {
            System.out.println("future cancelled: " + future.isCancelled());
            System.out.println("other cancelled: " + other.isCancelled());
            System.out.println("exceptionally called: " + cancelled.get());
            System.out.println("handle called: " + handled.get());
            System.out.println("handle called with value: " +
handleCalledWithValue.get());
        }
    }

(full gist:
https://gist.github.com/spullara/5897605#file-completablefuturetest-java-L59
)

The output with the latest lambda build, is this:

future cancelled: true
other cancelled: false
exceptionally called: false
handle called: true
handle called with value: true

What I would like it to be for cancellation to be useful and that this
program meets my expectations would be quite different:

future cancelled: true
other cancelled: true
exceptionally called: true
handle called: true
handle called with value: false

In order for that to be true, cancellation should cause the
CompletableFuture to complete exceptionally and then propagate that
cancellation to dependent futures. I can't really explain the current
behavior, especally the part where handle gets called as if everything
above it was successful. Truly the only effect that cancel() seems to have
is to make the top level future that is cancelled throw an exception and
report that it was cancelled. That doesn't seem like it is useful enough
for me to even build an interesting polling structure on top since only the
very top level would have access to it. Even worse, since you can't call
methods on a variable that is being assigned to from a lambda on the right
hand side, to even check this in my code I would have to refactor it.

Maybe it is just a bug? The output of this program is also baffling to me:

    @Test
    public void testExceptionally() {
        AtomicBoolean called = new AtomicBoolean();
        CompletableFuture<Object> future = new
CompletableFuture<>().exceptionally(t -> {
            called.set(true);
            return null;
        });
        future.completeExceptionally(new CancellationException());
        try {
            future.get();
        } catch (Exception e) {
            System.out.println("exceptionally called: " + called);
        }
    }

Output:
exceptionally called: false

Sam



> -Doug
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/lambda-libs-spec-experts/attachments/20130630/33301b42/attachment-0001.html 


More information about the lambda-libs-spec-experts mailing list