1 /*
   2  * Copyright (c) 2006, 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  */
  30 
  31 import java.util.*;
  32 import java.util.concurrent.*;
  33 
  34 /**
  35  * Adapted from Doug Lea, which was...
  36  * adapted from a posting by Tom Sugden tom at epcc.ed.ac.uk
  37  */
  38 public class BlockingTaskExecutor {
  39 
  40     static void realMain(String[] args) throws Throwable {
  41         for (int i = 1; i <= 100; i++) {
  42             System.out.print(".");
  43             test();
  44         }
  45     }
  46 
  47     static void test() throws Throwable {
  48         final ExecutorService executor = Executors.newCachedThreadPool();
  49 
  50         final NotificationReceiver notifiee1 = new NotificationReceiver();
  51         final NotificationReceiver notifiee2 = new NotificationReceiver();
  52 
  53         final Collection<Callable<Object>> tasks =
  54             new ArrayList<Callable<Object>>();
  55         tasks.add(new BlockingTask(notifiee1));
  56         tasks.add(new BlockingTask(notifiee2));
  57         tasks.add(new NonBlockingTask());
  58 
  59         // start a thread to invoke the tasks
  60         Thread thread = new Thread() { public void run() {
  61             try { executor.invokeAll(tasks); }
  62             catch (RejectedExecutionException t) {/* OK */}
  63             catch (Throwable t) { unexpected(t); }}};
  64         thread.start();
  65 
  66         // Wait until tasks begin execution
  67         notifiee1.waitForNotification();
  68         notifiee2.waitForNotification();
  69 
  70         // Now try to shutdown the executor service while tasks
  71         // are blocked.  This should cause the tasks to be
  72         // interrupted.
  73         executor.shutdownNow();
  74         if (! executor.awaitTermination(5, TimeUnit.SECONDS))
  75             throw new Error("Executor stuck");
  76 
  77         // Wait for the invocation thread to complete.
  78         thread.join(1000);
  79         if (thread.isAlive()) {
  80             thread.interrupt();
  81             thread.join(1000);
  82             throw new Error("invokeAll stuck");
  83         }
  84     }
  85 
  86     /**
  87      * A helper class with a method to wait for a notification.
  88      *
  89      * The notification is received via the
  90      * <code>sendNotification</code> method.
  91      */
  92     static class NotificationReceiver {
  93         /** Has the notifiee been notified? */
  94         boolean notified = false;
  95 
  96         /**
  97          * Notify the notification receiver.
  98          */
  99         public synchronized void sendNotification() {
 100             notified = true;
 101             notifyAll();
 102         }
 103 
 104         /**
 105          * Waits until a notification has been received.
 106          *
 107          * @throws InterruptedException if the wait is interrupted
 108          */
 109         public synchronized void waitForNotification()
 110             throws InterruptedException {
 111             while (! notified)
 112                 wait();
 113         }
 114     }
 115 
 116     /**
 117      * A callable task that blocks until it is interrupted.
 118      * This task sends a notification to a notification receiver when
 119      * it is first called.
 120      */
 121     static class BlockingTask implements Callable<Object> {
 122         private final NotificationReceiver notifiee;
 123 
 124         BlockingTask(NotificationReceiver notifiee) {
 125             this.notifiee = notifiee;
 126         }
 127 
 128         public Object call() throws InterruptedException {
 129             notifiee.sendNotification();
 130 
 131             // wait indefinitely until task is interrupted
 132             while (true) {
 133                 synchronized (this) {
 134                     wait();
 135                 }
 136             }
 137         }
 138     }
 139 
 140     /**
 141      * A callable task that simply returns a string result.
 142      */
 143     static class NonBlockingTask implements Callable<Object> {
 144         public Object call() {
 145             return "NonBlockingTaskResult";
 146         }
 147     }
 148 
 149     //--------------------- Infrastructure ---------------------------
 150     static volatile int passed = 0, failed = 0;
 151     static void pass() {passed++;}
 152     static void fail() {failed++; Thread.dumpStack();}
 153     static void fail(String msg) {System.out.println(msg); fail();}
 154     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
 155     static void check(boolean cond) {if (cond) pass(); else fail();}
 156     static void equal(Object x, Object y) {
 157         if (x == null ? y == null : x.equals(y)) pass();
 158         else fail(x + " not equal to " + y);}
 159     public static void main(String[] args) throws Throwable {
 160         try {realMain(args);} catch (Throwable t) {unexpected(t);}
 161         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
 162         if (failed > 0) throw new AssertionError("Some tests failed");}
 163 }
--- EOF ---