JDK-8286463 : DiscardPolicy may block invokeAll forever
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 8,11,17,18,19
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2022-05-09
  • Updated: 2022-05-10
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
OS version: Mac OS Montery 12.3.1
Java version: OpenJDK 64-Bit Server 25.322-b1

A DESCRIPTION OF THE PROBLEM :
DiscardPolicy just uses a no-op implementation to discard the submitted tasks, which causes rejected task remaining state NEW, which will block any get/invokeAll operation without time-bound parameters.

as far as I search, this is an issue similar with https://bugs.openjdk.java.net/browse/JDK-8160037.

According Doug's and Martin's  comment, programmer can cancel the future task to avoid forever blocking, but invokeAll blocks forever so no one has a chance to cancel a task.

Maybe DiscardPolicy and invokeAll are both implemented according to the spec, but they just don't work well together. Can someone change this mechanism so user can cancel the discarded task or let programmer get information from the API document so they just don't use 2 of them at the same time.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. use DiscardPolicy to initiate a thread pool executor.
2. submit a lot of jobs via invokeAll API.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
invokeAll returns all results as expected.
ACTUAL -
invokeAll blocks forever.

---------- BEGIN SOURCE ----------
public static void main(String[] args) {
        AtomicInteger counter = new AtomicInteger(0);
        // a very small thread pool which is easy to fulfilled
        ExecutorService testPool = new ThreadPoolExecutor(
                1,
                1,
                1,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1),
                r -> {
                    Thread thread = new Thread(r);
                    thread.setName("test-thread" + counter.getAndIncrement());
                    return thread;
                },
                new ThreadPoolExecutor.DiscardPolicy()
        );

                // gengerate several jobs here
        final List<Callable<Integer>> tasks = IntStream.range(1, 10).mapToObj((i) -> {
            return new Callable<Integer>() {
                /**
                 * Computes a result, or throws an exception if unable to do so.
                 *
                 * @return computed result
                 * @throws Exception if unable to compute a result
                 */
                @Override
                public Integer call() throws Exception {
                    // sleep to simulate long-run job
                    Thread.sleep(1000L);
                    return i;
                }
            };
        }).collect(Collectors.toList());
        try {
            testPool.invokeAll(tasks);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // you will never see this
        log.info("this executor can run through all tasks");
        testPool.shutdownNow();
        // you will never see this
        log.info("good bye");
    }
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
use DiscardOldestPolicy instead.

FREQUENCY : always



Comments
Like I said over on JDK-8160037 """perhaps there's something we can add to the docs""" ThreadPoolExecutor.DiscardPolicy should probably have some kind of warning that it's rarely useful in the real world. Reminds me that firewalls that silently discard suspicious connections cause connecting clients to hang. Which in our hostile world might be considered a feature!
10-05-2022

The observations on Windows 10: JDK 8: Failed, the reproducer blocked. JDK 11: Failed. JDK 17: Failed. JDK 18: Failed. JDK 19ea+19: Failed.
10-05-2022