The following code works with JDK16 reproducibly and hangs with JDK17 reproducibly on my laptop (4/8-core) with basic command line options: "-ea". A JDK-ticket exists (https://bugs.openjdk.org/browse/JDK-8281524), but there was disagreement whether the usage is "good" or not. Then, radio silence in the last 6 months. Can anybody help pinpoint my usage error (if any) and how to fix it?
import java.util.Arrays;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.Stream;
import org.junit.Test;
public class FjpTest {
@Test
public void testFjp() {
final ForkJoinPool fjPool = new ForkJoinPool(1);
final String[] objs = Stream.generate(() -> "").limit(10_000).toArray(String[]::new);
// the following line should sort the array,
// but instead causes a single-threaded spin-wait
fjPool.invoke(ForkJoinTask.adapt(() -> Arrays.parallelSort(objs))); // this hangs!
}
}
Update 1 (in response to Stephen C's comment):
Consider this example with considerable logic executed in a custom pool. This appears not only a "major loss of function" (hence "Priority=P3" on the Open-JDK ticket), because this worked in JDK16. It also displays an incompatibility within the JDK17 itself, because, we can't apparently use custom pools together with the collections framework anymore. I'm still not sure, how to solve this. The only thing I can conceive of is that anything that was originally designed to be run in the common pool must be explicitely submitted to the common pool, which would seem like a tough design choice. What am I overlooking?
new ForkJoinPool(1).invoke(ForkJoinTask.adapt(() -> {
// ... some really smart and deep business logic in a ginormous application using
// a custom ForkJoinPool, when an innocent developer or a library for that matter
// (not even realizing that it runs inside a custom ForkJoinPool) decides
// to use the collections framework ...
Arrays.parallelSort(Stream.generate(() -> "").limit(10_000).toArray(String[]::new));
// ... dead code from here ...
}));
My interpretation of what Doug Lea is saying on the ticket is that that cause of the problem is that your use-case is causing work sharing between two ForkJoinPool instances, and that was never guaranteed to work.
Now I can see why the sharing could be happening. The javadoc for public static <T extends Comparable<? super T>> void parallelSort(T[] a) state:
The
ForkJoincommon pool is used to execute any parallel tasks.
So by launching a task in a custom pool that calls parallelSort you are setting up the conditions for cross-pool work sharing.
The answer seems to be "don't do that". Don't call parallelSort from a task in a custom ForkJoinPool. The Arrays.parallelSort methods don't provide a way to specify the pool you want to use. Instead, just launch your tasks in the common pool.
I have tentatively confirmed this by changing:
final ForkJoinPool fjPool = new ForkJoinPool(1);
in your example to
final ForkJoinPool fjPool = ForkJoinPool.commonPool();
and it seems to work. This may not be the solution you want, but I think that it is the best you will get, unless you can convince Doug Lea et al that:
Arrays.parallelSort to use a specified ForkJoinPool.Personally ... I'm not convinced that either of those fixes would be a good idea.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With