[External] : Re: Changes to JEP 453

Ron Pressler ron.pressler at oracle.com
Tue May 23 18:27:25 UTC 2023



> On 23 May 2023, at 17:47, forax at univ-mlv.fr wrote:
> 
> 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.

Sure, but “give me the best successful result among these forks” doesn’t require treating their exceptions individually. It’s a somewhat more elaborate policy than ShutdownOnSuccess, and I don’t see how exception transparency for the individual forks would be useful.

> 
> 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.

Great, then do that transformation in the fork by wrapping the subtask. The code that wraps the fork is also the responsibility of the scope.

> 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.

STS itself doesn’t wrap any exceptions. The two policies throw their *own* exception when they fail and those exceptions may, indeed, wrap some fork exception, but it’s a failure of the scope, not of the forks.

While computing its own outcome, the scope examines the Subtasks, where it has access to the fork’s actual exceptions. There you can pattern match on exception types if you like. If you need to consider the actual heterogeneity of exception types among forks, do that processing inside the fork and throw some uniform exceptions.

But perhaps I just don’t understand what you’re saying. Can you show a code example (as short as possible) of what you think would be a common situation where exception transparency would be useful?

> 
> yes, but if you use the identity 
> SoS.result(e -> e)
> you will get a Throwable because the exception type has been erased.


But that’s what we want because we want to encourage the scope’s exception *not* being some fork’s exception! That method exist to allow you to wrap some chosen cause with your own exception type rather than ExecutionException.

We want exception transparency when we want to simply propagate an exception from one level to another, but STS was design to block precisely that. It wants exception opacity. When would exception transparency work? In an early prototype we had a structured scope that was only ever “shutdown on fail”, and the idea was that it’s the parallel counterpart to a foreach loop, where exceptions are also directly propagated. The contract was that fork failure *is* scope failure — i.e. a fork can try and directly fail the scope — and if the scope didn’t want some fork to fail it, you had to catch the exception by wrapping the callable. That’s where exception transparency would have been nice. 

We found that approach too clever for a mass audience, especially as a first structured concurrency construct, and decided to do something that might be more familiar and that in *most* situations would not require any wrapping of tasks, but may require it in less common situations. Among other things, that meant that forks’ exceptions are not the scope’s exception; rather the collects them, examines them, and then throws its own exception if it wants to fail.

— Ron


More information about the loom-dev mailing list