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

CompletableFuture.get may swallow InterruptedException

    XMLWordPrintable

    Details

    • Subcomponent:
    • Introduced In Version:
      9
    • Resolved In Build:
      b29
    • Verification:
      Verified

      Backports

        Description

        A DESCRIPTION OF THE PROBLEM :
        `CompletableFuture.get` may swallow `InterruptedException` if waiting thread is interrupted and waiting future completes immediately after.

        This issues exist in openjdk 9 and above, but not openjdk 8.

        I have created a github repository with analysis and testing code. See https://github.com/kezhuw/openjdk-completablefuture-interruptedexception if it helps.

        REGRESSION : Last worked in version 8u261

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        1. Calls `CompletableFuture.get` on note completed future future-a in thread-a.
        2. Interrupts thread-a and completes future-a.
        3. If future-a completes normally, the interrupt status may be swallowed in openjdk 9 and above.

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        If `CompletableFuture.get` completes normally, interrupt status should be keep.
        ACTUAL -
        After `CompletableFuture.get` completes normally, interrupt status may be swallowed.

        ---------- BEGIN SOURCE ----------
        import java.io.PrintStream;
        import java.util.concurrent.CompletableFuture;
        import java.util.concurrent.CountDownLatch;
        import java.util.concurrent.ExecutionException;
        import java.util.concurrent.ThreadLocalRandom;

        public class CompletableFutureGet0 {
            private static final PrintStream stdout = System.out;
            private static final PrintStream stderr = System.err;

            private static final String futureMethod = "get";
            private static final FutureWaiter futureWaiter = CompletableFuture::get;

            private static final int maxRuns = 1000;

            @FunctionalInterface
            interface FutureWaiter {
                void wait(CompletableFuture<Void> future) throws InterruptedException, ExecutionException;
            }

            public static void main(String[] args) throws Exception {
                for (int i = 1; i <= maxRuns; i++) {
                    long sleepMills = ThreadLocalRandom.current().nextLong(10);
                    final String sleepString = sleepMills == 0 ? "--------" : String.format("sleep(%d)", sleepMills);
                    final String prefix = String.format("%4d/%d interrupt-%s-complete", i, maxRuns, sleepString);

                    CompletableFuture<Void> future = new CompletableFuture<>();

                    CountDownLatch waitingFutureLatch = new CountDownLatch(1);
                    CountDownLatch futureGotLatch = new CountDownLatch(1);

                    Thread futureGetThread = new Thread(() -> {
                        try {
                            waitingFutureLatch.countDown();
                            futureWaiter.wait(future);
                            // XXX: Test whether interrupt status was lost.
                            if (Thread.currentThread().isInterrupted()) {
                                stdout.format("%s: future.%s completes, Thread.isInterrupted returns true\n", prefix, futureMethod);
                            } else {
                                stderr.format("%s: future.%s completes, Thread.isInterrupted returns false\n", prefix, futureMethod);
                                System.exit(1);
                            }
                        } catch (InterruptedException ex) {
                            stdout.format("%s: future.%s is interrupted.\n", prefix, futureMethod);
                            try {
                                futureWaiter.wait(future);
                            } catch (Exception ex1) {
                                stderr.println("Got unexpected exception");
                                ex.printStackTrace();
                                System.exit(128);
                            }
                        } catch (ExecutionException ex) {
                            stderr.println("Got unexpected execution exception");
                            ex.printStackTrace();
                            System.exit(128);
                        }
                        futureGotLatch.countDown();
                    }, String.format("future-get-thread-%d", i));
                    futureGetThread.setDaemon(true);
                    futureGetThread.start();

                    waitingFutureLatch.await();
                    Thread.sleep(1);

                    try {
                        futureGetThread.interrupt();
                        if (sleepMills > 0) {
                            Thread.sleep(sleepMills);
                        }
                        future.complete(null);
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }

                    futureGotLatch.await();
                }
            }
        }

        ---------- END SOURCE ----------

        FREQUENCY : often


          Attachments

            Issue Links

              Activity

                People

                Assignee:
                martin Martin Buchholz
                Reporter:
                webbuggrp Webbug Group
                Votes:
                0 Vote for this issue
                Watchers:
                7 Start watching this issue

                  Dates

                  Created:
                  Updated:
                  Resolved: