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