[External] : Re: Changes to JEP 453
forax at univ-mlv.fr
forax at univ-mlv.fr
Tue May 23 16:47:18 UTC 2023
----- Original Message -----
> From: "Ron Pressler" <ron.pressler at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "loom-dev" <loom-dev at openjdk.org>
> Sent: Tuesday, May 23, 2023 6:02:47 PM
> Subject: Re: [External] : Re: Changes to JEP 453
>> On 23 May 2023, at 15:03, Remi Forax <forax at univ-mlv.fr> wrote:
>>
>> If a STS has no a shutdown policy, I believe there is a missing method "result"
>> in SubTask that works like ShutdownOnSucess.result().
>> It may either return a result (SUCCESS), throw an ExceptionException (FAILED) or
>> throw UnavailableException (UNAVAILABLE).
>> Obviously, this method can be written with the current API (state() + get() or
>> exception()) but it will be very convenient to have it, and unify how the
>> different STSs work.
>
> First, if there’s no policy, then we’re either dealing with an advanced user or
> a very simple use case, but off the bat we’re dealing with the more exceptional
> situation (we may require a policy after the first preview).
>
> Second, the very use case you’re describing is not the one we’d like to
> encourage. If the exception is part of a successful result it should be
> returned, not thrown, and if it isn’t, it belongs in the phase where the scope
> (even without a policy) decides what its own state is. Separating successes
> from failures is good.
Returning the default value instead of an exception inside the callable is a useful simplification.
But it does not work in all cases, by example, if the default value of a callable is computed from the result of another async call belonging to the same scope.
There is quite a lot of business cases based on the idea that if some data are missing, they will be replaced by less accurate data available that are good enough.
>
>>
>> Exception transparency is orthogonal to a specific policy (or no policy), it's a
>> way to avoid exception erasure (exception seen as a Throwable) and its wrapping
>> into ExecutionException.
>> The wrapping into ExecutionException is quite bad because make the composition
>> of several STSs awkward because the type of the lambda submitted to a fork and
>> the type of exception thrown by the method containing the try-with-resources
>> with a STS are not the same.
>>
>> Exception transparency is a way to replace the wrapping ExceptionException by a
>> more precise exception tracked by the type system enabling easier composition.
>
> I’m confused here. An STS is used when we want to treat multiple subtasks as a
> unit. If the scope fails, then it fails because its policy (whether implemented
> as an actual subclass of STS or inline without a subclass) decides it should
> fail, its exception should not be any of its forks exception, and at the very
> least should be wrapped to include the owner thread’s stack dump. All the
> things you’re mentioning are there by design to support the particular use of
> STS.
>
> In particular, that the type of exception thrown by the fork and that the type
> of exception thrown by some result composition method are not the same is
> intentional; even if we had exception transparency, we wouldn't want it here,
> because it’s not supposed to be the same exception type (and it’s supposed to
> be an exception generated by the scope’s owner thread). It also couldn’t be the
> same exception type: What exception should, say, SoS.result throw if, out of
> five forks, three forks throw IOException and the two others throw some
> RuntimeException?
>
> If you care about handling a particular exception type in a particular fork
> differently from other types, that code belongs inside the fork, because that’s
> where its individuality is retained. So you’d do something like:
>
> scope.fork(() -> {
> try { return foo(); } catch (IOException ex) { return null; }});
>
> Now, if you get an ExecutionException, that means that the scope has failed to
> yield a result, and is supposed to not say anything about the forks. But if for
> some reason or another you wish to explore the cause of the exception further,
> then future enhancements to pattern matching could help; e.g. we may have
> `catch (ExecutionExeption(IOException ex)) { … }`. Of course you’re also free
> to create any exception you like in the catch block and rethrow that.
When you implement a REST API, you have the problem of mapping the error to the right HTTP error code, so in Java, an exception subclass to the right HTTP code.
Most frameworks/libraries centralize this mapping per service (which is a good idea), so it's usual to have a code inside the callable to wrap the exception to a specific business exception (DeliveryServiceException, UserMappingServiceException, etc) that will be used to map to the correct HTTP error code.
In that setup, it's more useful to get the exception thrown by the callable than a wrapping exception. And if you want to log the exception without loosing the context, you can wrap it.
>
> SoF.throwIfFailed, and SoS.result have variants that allow you to manipulate the
> type of exception thrown by the scope if you don’t like ExecutionError.
yes, but if you use the identity
SoS.result(e -> e)
you will get a Throwable because the exception type has been erased.
>
> — Ron
Rémi
More information about the loom-dev
mailing list