1 /*
   2  * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 6431315
  27  * @summary ExecutorService.invokeAll might hang
  28  * @author Martin Buchholz
  29  * @library /test/lib
  30  */
  31 
  32 import static java.util.concurrent.TimeUnit.MILLISECONDS;
  33 
  34 import java.util.ArrayList;
  35 import java.util.Collection;
  36 import java.util.concurrent.Callable;
  37 import java.util.concurrent.ExecutorService;
  38 import java.util.concurrent.Executors;
  39 import java.util.concurrent.RejectedExecutionException;
  40 import jdk.test.lib.Utils;
  41 
  42 /**
  43  * Adapted from Doug Lea, which was...
  44  * adapted from a posting by Tom Sugden tom at epcc.ed.ac.uk
  45  */
  46 public class BlockingTaskExecutor {
  47     static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
  48 
  49     static void realMain(String[] args) throws Throwable {
  50         for (int i = 1; i <= 100; i++) {
  51             System.out.print(".");
  52             test();
  53         }
  54     }
  55 
  56     static void test() throws Throwable {
  57         final ExecutorService executor = Executors.newCachedThreadPool();
  58 
  59         final NotificationReceiver notifiee1 = new NotificationReceiver();
  60         final NotificationReceiver notifiee2 = new NotificationReceiver();
  61 
  62         final Collection<Callable<Object>> tasks = new ArrayList<>();
  63         tasks.add(new BlockingTask(notifiee1));
  64         tasks.add(new BlockingTask(notifiee2));
  65         tasks.add(new NonBlockingTask());
  66 
  67         // start a thread to invoke the tasks
  68         Thread thread = new Thread() { public void run() {
  69             try { executor.invokeAll(tasks); }
  70             catch (RejectedExecutionException t) {/* OK */}
  71             catch (Throwable t) { unexpected(t); }}};
  72         thread.start();
  73 
  74         // Wait until tasks begin execution
  75         notifiee1.waitForNotification();
  76         notifiee2.waitForNotification();
  77 
  78         // Now try to shutdown the executor service while tasks
  79         // are blocked.  This should cause the tasks to be
  80         // interrupted.
  81         executor.shutdownNow();
  82         if (! executor.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
  83             throw new Error(
  84                 String.format("Executor termination timed out after %d ms",
  85                               LONG_DELAY_MS));
  86 
  87         // Wait for the invocation thread to complete.
  88         thread.join(LONG_DELAY_MS);
  89         if (thread.isAlive()) {
  90             thread.interrupt();
  91             thread.join(LONG_DELAY_MS);
  92             throw new Error(
  93                 String.format("invokeAll timed out after %d ms",
  94                               LONG_DELAY_MS));
  95         }
  96     }
  97 
  98     /**
  99      * A helper class with a method to wait for a notification.
 100      *
 101      * The notification is received via the
 102      * {@code sendNotification} method.
 103      */
 104     static class NotificationReceiver {
 105         /** Has the notifiee been notified? */
 106         boolean notified = false;
 107 
 108         /**
 109          * Notify the notification receiver.
 110          */
 111         public synchronized void sendNotification() {
 112             notified = true;
 113             notifyAll();
 114         }
 115 
 116         /**
 117          * Waits until a notification has been received.
 118          *
 119          * @throws InterruptedException if the wait is interrupted
 120          */
 121         public synchronized void waitForNotification()
 122             throws InterruptedException {
 123             while (! notified)
 124                 wait();
 125         }
 126     }
 127 
 128     /**
 129      * A callable task that blocks until it is interrupted.
 130      * This task sends a notification to a notification receiver when
 131      * it is first called.
 132      */
 133     static class BlockingTask implements Callable<Object> {
 134         private final NotificationReceiver notifiee;
 135 
 136         BlockingTask(NotificationReceiver notifiee) {
 137             this.notifiee = notifiee;
 138         }
 139 
 140         public Object call() throws InterruptedException {
 141             notifiee.sendNotification();
 142 
 143             // wait indefinitely until task is interrupted
 144             while (true) {
 145                 synchronized (this) {
 146                     wait();
 147                 }
 148             }
 149         }
 150     }
 151 
 152     /**
 153      * A callable task that simply returns a string result.
 154      */
 155     static class NonBlockingTask implements Callable<Object> {
 156         public Object call() {
 157             return "NonBlockingTaskResult";
 158         }
 159     }
 160 
 161     //--------------------- Infrastructure ---------------------------
 162     static volatile int passed = 0, failed = 0;
 163     static void pass() {passed++;}
 164     static void fail() {failed++; Thread.dumpStack();}
 165     static void fail(String msg) {System.out.println(msg); fail();}
 166     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
 167     static void check(boolean cond) {if (cond) pass(); else fail();}
 168     static void equal(Object x, Object y) {
 169         if (x == null ? y == null : x.equals(y)) pass();
 170         else fail(x + " not equal to " + y);}
 171     public static void main(String[] args) throws Throwable {
 172         try {realMain(args);} catch (Throwable t) {unexpected(t);}
 173         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
 174         if (failed > 0) throw new AssertionError("Some tests failed");}
 175 }