Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8301216

ForkJoinPool invokeAll() ignores timeout

XMLWordPrintable

    • b05
    • 17
    • b01
    • generic
    • generic

        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


              wxiao Weibing Xiao
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              7 Start watching this issue

                Created:
                Updated:
                Resolved: