JDK-8319498 : ForkJoinPool.invoke(ForkJoinTask) does not specify behavior when task throws checked exception
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 22
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2023-11-06
  • Updated: 2023-12-11
  • Resolved: 2023-12-06
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 22
22 b27Fixed
Related Reports
Relates :  
Description
ForkJoinPool.invoke(ForkJoinTask) specifies that it rethrows errors and unchecked exceptions. It doesn't specify the behavior for tasks that throw a checked exception. The behavior in JDK 19-21 was to throw RuntimeException with the checked exception as the cause, this is probably the only sane behavior can that can be specified for this scenario.
Comments
Changeset: cc25d8b1 Author: Doug Lea <dl@openjdk.org> Date: 2023-12-06 16:12:59 +0000 URL: https://git.openjdk.org/jdk/commit/cc25d8b12bbab9dde9ade7762927dcb8d27e23c5
06-12-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/16725 Date: 2023-11-19 17:36:01 +0000
21-11-2023

Yes. The solution was to use updated execution rules, but to trap the final return. Added as part of https://github.com/openjdk/jdk/pull/16725
21-11-2023

[~dl] and [~alanb] came up with a solution for this yesterday, so hopefully this can be resolved soon.
21-11-2023

Oh, now I see the issue. Unlike invokeAny/All there is no ExecutorService method to conform to here. So the question is whether to confusingly make this unlike jdk22 invokeAny/All by reverting to old behavior even when invoked by external callers, or to adjust the spec as an extension of JDK-8288899 CCR. Neither seems attractive. Or I suppose. to avoid answering this andadjust the test to ensure that the caller is an internal ForkJoin thread, in which case it is intended to be compatible with existing spec.
19-11-2023

try (ForkJoinPool pool = new ForkJoinPool()) { pool.invoke(ForkJoinTask.adaptInterruptible(() -> {throw new IOException();})); } ForkJoinPool::invoke asserts "If the computation encounters an unchecked Exception or Error, it is rethrown as the outcome of this invocation." and ForkJoinTask::adaptInterruptible task is expected to be "translating any checked exceptions encountered into RuntimeException" So the expectations from the sample above were to get a RuntimeException thrown by ForkJoinPool::invoke, this was true for and confirmed by OpenJDK 19, 20, 21 implementations, but however is no longer the case and is violation of the current set of specifications.
16-11-2023

This issue is coming up with pool.invoke(ForkJoinTask.adaptInterruptible(Callable)). The adaptInterruptible(Callable) method translates a checked exception into a RuntimeException. The unwrapping means that ForkJoinPool.invoke and ForkJoinTask.join can throw any checked exception so hard to deal with at compile-time (similar problem to Class::newInstance).
07-11-2023

I resist doing anything about this, because the initial spec flexibility is the main reason we were able to evolve FJP to accommodate different tasks and callers -- similar issues may arise in the future. However, the tck tests were broken in that they imposed only one of the allowed outcomes.
06-11-2023