Feedback: FailedException and Joiner
Anthony Vanelverdinghe
dev at anthonyv.be
Fri Aug 8 14:18:59 UTC 2025
On 8/8/2025 2:40 PM, Alan Bateman wrote:
>
> On 08/08/2025 12:56, Anthony Vanelverdinghe wrote:
>> Here's my feedback from upgrading my Loom experiments to JDK 25:
>
> Can you provide a summary of your experiments? The most useful
> feedback is usually from experiments with real world applications and
> scenarios so it would be interesting to know more about your usages, a
> bit on what the subtasks do and what they return (the focus on the
> latter is to get some sense on whether the subtasks return results of
> the same types or different types).
>
> -Alan
Sure. It's just a single scenario and not a real-world application
though: a basic grep. So each subtask is a `Callable<T>` that reads a
chunk of a file and returns a T (a Boolean to indicate whether the chunk
contained a match, a Long with the number of matches, a record with the
lines that matched). And then there's a `Collector` to collect the
results (OR'ing the Booleans, summing the Longs, returning a
`Stream<String>`).
Previously I had written a `class DefaultSts extends
StructuredTaskScope` which had a method `results()` returning a `record
Results<R, S, U>(Reason reason, R successResult, S failedResult, U
cancelledResult) {}`. So you'd switch on the `reason` to determine what
to do (this dates back to JDK 19, hence the `cancelledResult`).
So before I had: `var scope = DefaultSts.of(collector)` and `return
scope.results()`
And now I have: `var scope =
StructuredTaskScope.open(Joiners.ofThrowing(collector))` and `return
scope.join()`
which is the same as: `var scope =
StructuredTaskScope.open(StructuredTaskScope.Joiner.<T>allSuccessfulOrThrow())`
and `return scope.join().map(Subtask::get).collect(collector)`
Before I would have an exhaustive switch on `Reason` and thus always be
reminded to do error handling. As mentioned, I was surprised that my
code compiled without error handling now.
The actual checked exceptions that get thrown from my subtasks are
`ExecutionException` and `InterruptedException`, from invoking
`Future::get` on the result of `AsynchronousFileChannel::read`. So if an
`IOException` were to occur, it would be at
`FailedException(ExecutionException(IOException))`.
An issue with the current `join` is also that I typically only want to
deal with checked exceptions and let unchecked exceptions bubble up. So
I'd need a utility method and something like `catch(FailedException e) {
var cause = getCauseAndThrowIfUnchecked(e); ... }`.
While I could still have the `Joiner` return something to switch on,
analogous to what I did previously, `join` is now designed for failure
handling, so I'd rather use that mechanism. By having two methods,
`joinChecked` and `joinUnchecked`, a developer would be able to
"assert" whether their subtasks throw checked exceptions.
Kind regards, Anthony
More information about the loom-dev
mailing list