Changes to JEP 453
Ron Pressler
ron.pressler at oracle.com
Tue May 23 13:05:11 UTC 2023
Hi.
The ensuing discussion has led us to another change of heart, as many of the cases that were brought up were about uses of StructuredTaskScope in ways for which it wasn't intended. Since STS is the first explicit introduction of structured concurrency to the JDK, we feel it is important to steer users to its intended use. In particular, when tracking the state of individual forks, we should report the state *only insofar as the scope should be concerned*.
The scope treats the forks as a unit and must not concern itself with forks that cannot contribute to the scope's own outcome. The scope, preferably through a subclass implementing a shutdown policy, should describe how the forks' outcomes are composed to produce the scope's outcome but should show no further interest in the outcome of the individual forks. The code inside the scope's block performs not only the joining and handling of results, but also the forking, and so manipulating individual forks through functional composition (i.e. by wrapping the forked subtask) is always possible, and is an appropriate use of the API. Therefore:
1. A call to shutdown indicates that the scope has no interest in further results. Consequently, handleComplete will not be called for tasks that have not completed before shutdown (including tasks that were forked after shutdown); as before, handleComplete will only be called for forks in the SUCCESS/FAILED state. Tracking the state of individual forks following a shutdown for observability purposes (such as logging) should be done in the fork itself (this is always possible through functional composition).
2. While we recommend using a shutdown policy, we (still) don't require one. When a policy isn't used, the purpose of Subtask.state() is only to indicate whether it is legal to call Subtask.get() xor Subtask.exception(), and so Subtask.State now has only three values: SUCCESS (get() will return successfully), FAILED (exception() will return successfully), and UNAVAILABLE (neither method may be called). The UNAVAILABLE state indicates one of the following: 1. the subtask was forked but has not yet completed, 2. the subtask completed after shutdown, 3. the subtask was forked after shutdown and has therefore not started. An UNAVAILABLE state may or may not change to SUCCESS/FAILED (in practice, it will not change after join completes *normally*).
As for exceptions, an exception in any fork may either contribute to the scope's ultimate failed outcome (as in the case of ShutdownOnFailure and ShutdownOnSuccess) or be turned into a value that will be composed into the result (as in the runAll example in the JEP). In the latter case, the intent of "I want to use the exception of this fork as a value", should be expressed by returning that exception from the fork rather than by throwing it; a fork that contributes positively to the scope's successful result is, after all, a successful fork. Even with some exception transparency mechanism (including as a future language feature), the type of an individual fork's exception could only matter only in that latter case, and then its type could be expressed through the existing single type parameter (fork's U / Subtask's T).
The JEP and the Javadoc now reflect these changes.
We may rename Subtask yet again before this JEP is targeted.
-- Ron
> On 18 May 2023, at 19:59, Ron Pressler <ron.pressler at oracle.com> wrote:
>
> Hi.
>
> We have made several changes to structured concurrency (JEP 453) due to feedback:
>
> 1. We've renamed `TaskHandle` to `Subtask`. I can't promise it's the last rename :)
>
> 2. We've fixed the generic signature of handleComplete (thank you Rémi for pointing out the mistake)
>
> 3. We've changed the states and behaviour of subtasks on cancellation. Subtasks that are forked after `shutdown` is called will have the `STILLBORN` state. Running subtasks will be interrupted, but the state of their `Subtask` will remain `RUNNING` until completed, probably sometime after `join` has returned, because `join` returns immediately when `shutdown` is called. When completed, their `Subtasks` will be passed to `handleComplete`, either in the `FAILED` or `SUCCESS` state. I.e., Unlike `Future`, the state reflects the state of the task itself, not of the handle.
>
> 4. We've added the com.sun.management.Threads.currentThreadEnclosingScopes() method that returns a string with the description of the current structured context -- i.e. the stack trace for the current thread and the enclosing scopes with their owners' respective stack traces -- all the way up the hierarchy.
>
> https://openjdk.org/jeps/453
>
>
> -- Ron
>
More information about the loom-dev
mailing list