JDK-8301216 : ForkJoinPool invokeAll() ignores timeout
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 17,18
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2023-01-25
  • Updated: 2025-02-13
  • Resolved: 2023-03-01
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 17
17.0.8-oracle b01Fixed
Related Reports
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
Whenever using ForkJoinPool executor and calling invokeAll() method with a timeout less than the time the task takes to execute, it won't cancel the last task (or single), it just executes it until completion.

This bug was observed in any release of Java 17 only.

REGRESSION : Last worked in version 16

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
If the task sleep timer is greater than the timeout set, the task should always timeout. 
I have provided a source code scratch file to test this behaviour. If you run it with Java 11,12,13,14,15,16 or even 19. The console should always print "TASK TIMED OUT" (if it's setup with sleep greater than the timeout).

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
For all Callable tasks to be timed out. This is observed by "TASK TIMED OUT" being printed on the console for the number of tasks in the list.
ACTUAL -
The last or single (if only 1 passed to the list) task will print "TASK SUCCESSFULLY EXECUTED" on the console.

---------- BEGIN SOURCE ----------
import java.util.List;
import java.util.concurrent.*;

class Scratch {

    static final int TIME_OUT = 1000;
    static final TimeUnit TIME_OUT_UNIT = TimeUnit.MILLISECONDS;
    static final int TASK_SLEEP = 2000;
    
    public static void main(String[] args) {

        System.out.println("JVM: " + System.getProperty("java.version"));

        ExecutorService threadPool = new ForkJoinPool(3); // incorrect behaviour in Java 17
//        ExecutorService threadPool = Executors.newWorkStealingPool(); // incorrect behaviour in Java 17
//        ExecutorService threadPool = Executors.newFixedThreadPool(3); // works in Java 17

        Callable<String> simpleCallableTask = () -> {
            Thread.sleep(TASK_SLEEP); // task duration
            return "Return task result";
        };

        List<Callable<String>> callableList = List.of(simpleCallableTask, simpleCallableTask, simpleCallableTask);

        try {
            List<Future<String>> futureList = threadPool.invokeAll(callableList, TIME_OUT, TIME_OUT_UNIT);
            for (Future<String> future : futureList) {
                if (future.isCancelled()) {
                    System.out.println("TASK TIMED OUT");
                } else {
                    System.out.println("TASK SUCCESSFULLY EXECUTED");
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
It's mentioned with comments on the source code, but a workaround is not using ForkJoinPool at all, just use Executors.newFixedThreadPool(n)

FREQUENCY : always



Comments
A pull request was submitted for review. URL: https://git.openjdk.org/jdk17u-dev/pull/1329 Date: 2023-05-08 09:15:38 +0000
09-05-2023

Fix request (17u) I would like to fix this issue in jdk17u. The fix is small. It just cancels the current task when awaitPoolInvoke times out. The reproducer succeeds with this fix. The fix passed our CI testing. This includes most JCK and JTREG tiers 1-4 on the standard platforms and also on ppc64le.
09-05-2023

The observations on Windows 10: JDK 11: Passed. JDK 17ea+4: Passed. JDK 17ea+5: Failed, TASK SUCCESSFULLY EXECUTED observed. JDK 18: Failed. JDK 19: Passed. JDK 20ea+23: Passed.
27-01-2023

I think this one was fixed by the FJP refresh in JDK 19 (JDK-8277090). In JDK 17 and 18, it didn't cancel the task when awaitPoolInvoke timed out, so for the reproducer here, all but one task is cancelled.
27-01-2023