1 /*
   2  * Copyright (c) 2004, 2015, 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 5093922 2120055
  27  * @summary Test that NotificationBroadcasterSupport can be subclassed
  28  * and used with synchronized(this) without causing deadlock
  29  * @author Eamonn McManus
  30  * @modules java.management
  31  * @run clean BroadcasterSupportDeadlockTest
  32  * @run build BroadcasterSupportDeadlockTest
  33  * @run main BroadcasterSupportDeadlockTest
  34  */
  35 
  36 import java.lang.management.*;
  37 import java.util.concurrent.*;
  38 import javax.management.*;
  39 
  40 public class BroadcasterSupportDeadlockTest {
  41     public static void main(String[] args) throws Exception {
  42         try {
  43             Class.forName(ManagementFactory.class.getName());
  44         } catch (Throwable t) {
  45             System.out.println("TEST CANNOT RUN: needs JDK 5 at least");
  46             return;
  47         }
  48 
  49         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
  50         final BroadcasterMBean mbean = new Broadcaster();
  51         final ObjectName name = new ObjectName("test:type=Broadcaster");
  52         mbs.registerMBean(mbean, name);
  53 
  54         ThreadMXBean threads = ManagementFactory.getThreadMXBean();
  55         threads.setThreadContentionMonitoringEnabled(true);
  56 
  57         final Semaphore semaphore = new Semaphore(0);
  58 
  59         // Thread 1 - block the Broadcaster
  60         Thread t1 = new Thread() {
  61             public void run() {
  62                 try {
  63                     mbs.invoke(name, "block",
  64                                new Object[] {semaphore},
  65                                new String[] {Semaphore.class.getName()});
  66                 } catch (Exception e) {
  67                     e.printStackTrace(System.out);
  68                 } finally {
  69                     System.out.println("TEST INCORRECT: block returned");
  70                     System.exit(1);
  71                 }
  72             }
  73         };
  74         t1.setDaemon(true);
  75         t1.start();
  76 
  77         /* Wait for Thread 1 to be doing Object.wait().  It's very
  78            difficult to synchronize properly here so we wait for the
  79            semaphore, then wait a little longer for the mbs.invoke to
  80            run, then just in case that isn't enough, we wait for the
  81            thread to be in WAITING state.  This isn't foolproof,
  82            because the machine could be very slow and the
  83            Thread.getState() could find the thread in WAITING state
  84            due to some operation it does on its way to the one we're
  85            interested in.  */
  86         semaphore.acquire();
  87         Thread.sleep(100);
  88         while (t1.getState() != Thread.State.WAITING)
  89             Thread.sleep(1);
  90 
  91         // Thread 2 - try to add a listener
  92         final NotificationListener listener = new NotificationListener() {
  93             public void handleNotification(Notification n, Object h) {}
  94         };
  95         Thread t2 = new Thread() {
  96             public void run() {
  97                 try {
  98                     mbs.addNotificationListener(name, listener, null, null);
  99                 } catch (Exception e) {
 100                     System.out.println("TEST INCORRECT: addNL failed:");
 101                     e.printStackTrace(System.out);
 102                 }
 103             }
 104         };
 105         t2.setDaemon(true);
 106         t2.start();
 107 
 108         /* Wait for Thread 2 to be blocked on the monitor or to
 109            succeed.  */
 110         Thread.sleep(100);
 111 
 112         for (int i = 0; i < 1000/*ms*/; i++) {
 113             t2.join(1/*ms*/);
 114             switch (t2.getState()) {
 115             case TERMINATED:
 116                 System.out.println("TEST PASSED");
 117                 return;
 118             case BLOCKED:
 119                 java.util.Map<Thread,StackTraceElement[]> traces =
 120                     Thread.getAllStackTraces();
 121                 showStackTrace("Thread 1", traces.get(t1));
 122                 showStackTrace("Thread 2", traces.get(t2));
 123                 System.out.println("TEST FAILED: deadlock");
 124                 System.exit(1);
 125                 break;
 126             default:
 127                 break;
 128             }
 129         }
 130 
 131         System.out.println("TEST FAILED BUT DID NOT NOTICE DEADLOCK");
 132         Thread.sleep(10000);
 133         System.exit(1);
 134     }
 135 
 136     private static void showStackTrace(String title,
 137                                        StackTraceElement[] stack) {
 138         System.out.println("---" + title + "---");
 139         if (stack == null)
 140             System.out.println("<no stack trace???>");
 141         else {
 142             for (StackTraceElement elmt : stack)
 143                 System.out.println("    " + elmt);
 144         }
 145         System.out.println();
 146     }
 147 
 148     public static interface BroadcasterMBean {
 149         public void block(Semaphore semaphore);
 150     }
 151 
 152     public static class Broadcaster
 153             extends NotificationBroadcasterSupport
 154             implements BroadcasterMBean {
 155         public synchronized void block(Semaphore semaphore) {
 156             Object lock = new Object();
 157             synchronized (lock) {
 158                 try {
 159                     // Let the caller know that it can now wait for us to
 160                     // hit the WAITING state
 161                     semaphore.release();
 162                     lock.wait(); // block forever
 163                 } catch (InterruptedException e) {
 164                     System.out.println("TEST INCORRECT: lock interrupted:");
 165                     e.printStackTrace(System.out);
 166                     System.exit(1);
 167                 }
 168             }
 169         }
 170     }
 171 }