-
Bug
-
Resolution: Fixed
-
P3
-
17, 18
-
b05
-
b01
-
generic
-
generic
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8307605 | 17.0.8 | Richard Reingruber | P3 | Resolved | Fixed | b03 |
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
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
- backported by
-
JDK-8307605 ForkJoinPool invokeAll() ignores timeout
- Resolved
- relates to
-
JDK-8277090 jsr166 refresh for jdk19
- Resolved