Structured concurrency: TaskHandle

Alan Bateman Alan.Bateman at oracle.com
Fri May 12 12:37:31 UTC 2023


On 12/05/2023 11:39, Remi Forax wrote:
> I really like the fact that STS.fork() does not return a Future but a TaskHandle because as the JEP [1] said, the idea is to only give accesss to the resulting value (get()) or the exception once join() is called. But I think this approach can be refined following several axis.
>
> 1. I believe the method state() should also work like get()/exception(), i.e. if a user call state() before calling join(), an ISE should be thrown.
> Practically, it means that state() can not return RUNNING so the enum State can be simplified with only 3 states SUCCESS, FAILED and CANCELLED.
Having "get state" method throw ISE would be a bit strange, and I think 
reduce debugging to the toString method.

For the most part, you should find that you either don't need the return 
from fork, or you just treat it as a Supplier and get the result when 
you know the task has completed successfully.

> 2. I believe CANCELLED is a weird state. First, a lot of other asynchronous libraries merge FAILED and CANCELLED. Then, when shutdown() is called, from a user POV, the corresponding state can be either CANCELLED or FAILED(with an exception InterruptedException). So as a user, getting CANCELLED as result of state() is not enough to know if the task was shutdown or not. I propose to remove the state CANCELLED and usee FAILED + a newly created InterruptedException instead.

I wouldn't expect a non-deterministic state with TaskHandle when you 
shutdown. Maybe that comment is about when the API used Future where 
this was the case?

There may be merit in dropping CANCELLED but it does create an 
inconsistency in that handleComplete is only called to handle tasks that 
completely successfully or fail. If the state were dropped then would 
you expect a storm of calls to handleComplete for failed-with-IE tasks?


>
> 3. TaskHandle is a mutable "active object", storing it (by example in a queue) is a common error. TaskHandle should give access to an immutable result object, a record like object that represent SUCCESS (T value) | FAILED(Throwable exception) instead of providing the the methods get()/exception(). With that, handleComplete() should take this result object as parameter instead of TaskHandle so users have less chance to store TaskHandle in a collection.
We've explored several APIs and shapes, including Supplier<Result> and 
variations that included ADT for the Result. When you work through 
examples with heterogenous and homogenous tasks then what we've got now 
isn't too bad.


> An interesting followup question is should the TaskHandle objects should be invalidated when close() is called. While it would be nice to have this behavior, it means that it extends the lifetime of the TaskHandle objects so i've rule out that idea.
close does a shutdown so it will "cancel" any tasks that have not 
complete.  In any case, I don't think we can anticipate all the possible 
usages and I could imagine some assigning to a TaskHandle that is 
outside the scope for some reason or another.

-Alan




More information about the loom-dev mailing list