1 /*
   2  * Copyright (c) 2005, 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 6303187
  27  * @summary Test that no locks are held when a monitor attribute is sampled
  28  * or notif delivered.
  29  * @author Eamonn McManus
  30  * @key intermittent
  31  * @modules java.management
  32  * @run clean StringMonitorDeadlockTest
  33  * @run build StringMonitorDeadlockTest
  34  * @run main StringMonitorDeadlockTest 1
  35  * @run main StringMonitorDeadlockTest 2
  36  * @run main StringMonitorDeadlockTest 3
  37  * @run main StringMonitorDeadlockTest 4
  38  */
  39 
  40 import java.lang.management.ManagementFactory;
  41 import java.util.concurrent.atomic.AtomicInteger;
  42 import javax.management.JMX;
  43 import javax.management.MBeanServer;
  44 import javax.management.Notification;
  45 import javax.management.NotificationListener;
  46 import javax.management.ObjectName;
  47 import javax.management.monitor.StringMonitor;
  48 import javax.management.monitor.StringMonitorMBean;
  49 
  50 public class StringMonitorDeadlockTest {
  51 
  52     public static void main(String[] args) throws Exception {
  53         if (args.length != 1)
  54             throw new Exception("Arg should be test number");
  55         int testNo = Integer.parseInt(args[0]) - 1;
  56         TestCase test = testCases[testNo];
  57         System.out.println("Test: " + test.getDescription());
  58         test.run();
  59         System.out.println("Test passed");
  60     }
  61 
  62     private static enum When {IN_GET_ATTRIBUTE, IN_NOTIFY};
  63 
  64     private static abstract class TestCase {
  65         TestCase(String description, When when) {
  66             this.description = description;
  67             this.when = when;
  68         }
  69 
  70         void run() throws Exception {
  71             final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
  72             final ObjectName observedName = new ObjectName("a:b=c");
  73             final ObjectName monitorName = new ObjectName("a:type=Monitor");
  74             mbs.registerMBean(new StringMonitor(), monitorName);
  75             final StringMonitorMBean monitorProxy =
  76                 JMX.newMBeanProxy(mbs, monitorName, StringMonitorMBean.class);
  77             final TestMBean observedProxy =
  78                 JMX.newMBeanProxy(mbs, observedName, TestMBean.class);
  79 
  80             final Runnable sensitiveThing = new Runnable() {
  81                 public void run() {
  82                     doSensitiveThing(monitorProxy, observedName);
  83                 }
  84             };
  85 
  86             final Runnable nothing = new Runnable() {
  87                 public void run() {}
  88             };
  89 
  90             final Runnable withinGetAttribute =
  91                 (when == When.IN_GET_ATTRIBUTE) ? sensitiveThing : nothing;
  92 
  93             mbs.registerMBean(new Test(withinGetAttribute), observedName);
  94             monitorProxy.addObservedObject(observedName);
  95             monitorProxy.setObservedAttribute("Thing");
  96             monitorProxy.setStringToCompare("old");
  97             monitorProxy.setGranularityPeriod(10L); // 10 ms
  98             monitorProxy.setNotifyDiffer(true);
  99 
 100             final int initGetCount = observedProxy.getGetCount();
 101             monitorProxy.start();
 102 
 103             int getCount = initGetCount;
 104             for (int i = 0; i < 500; i++) { // 500 * 10 = 5 seconds
 105                 getCount = observedProxy.getGetCount();
 106                 if (getCount != initGetCount)
 107                     break;
 108                 Thread.sleep(10);
 109             }
 110             if (getCount <= initGetCount)
 111                 throw new Exception("Test failed: presumable deadlock");
 112             // This won't show up as a deadlock in CTRL-\ or in
 113             // ThreadMXBean.findDeadlockedThreads(), because they don't
 114             // see that thread A is waiting for thread B (B.join()), and
 115             // thread B is waiting for a lock held by thread A
 116 
 117             // Now we know the monitor has observed the initial value,
 118             // so if we want to test notify behaviour we can trigger by
 119             // exceeding the threshold.
 120             if (when == When.IN_NOTIFY) {
 121                 final AtomicInteger notifCount = new AtomicInteger();
 122                 final NotificationListener listener = new NotificationListener() {
 123                     public void handleNotification(Notification n, Object h) {
 124                         Thread t = new Thread(sensitiveThing);
 125                         t.start();
 126                         try {
 127                             t.join();
 128                         } catch (InterruptedException e) {
 129                             throw new RuntimeException(e);
 130                         }
 131                         notifCount.incrementAndGet();
 132                     }
 133                 };
 134                 mbs.addNotificationListener(monitorName, listener, null, null);
 135                 observedProxy.setThing("new");
 136                 for (int i = 0; i < 500 && notifCount.get() == 0; i++)
 137                     Thread.sleep(10);
 138                 if (notifCount.get() == 0)
 139                     throw new Exception("Test failed: presumable deadlock");
 140             }
 141 
 142         }
 143 
 144         abstract void doSensitiveThing(StringMonitorMBean monitorProxy,
 145                                        ObjectName observedName);
 146 
 147         String getDescription() {
 148             return description;
 149         }
 150 
 151         private final String description;
 152         private final When when;
 153     }
 154 
 155     private static final TestCase[] testCases = {
 156         new TestCase("Remove monitored MBean within monitored getAttribute",
 157                      When.IN_GET_ATTRIBUTE) {
 158             @Override
 159             void doSensitiveThing(StringMonitorMBean monitorProxy,
 160                                   ObjectName observedName) {
 161                 monitorProxy.removeObservedObject(observedName);
 162             }
 163         },
 164         new TestCase("Stop monitor within monitored getAttribute",
 165                      When.IN_GET_ATTRIBUTE) {
 166             @Override
 167             void doSensitiveThing(StringMonitorMBean monitorProxy,
 168                                   ObjectName observedName) {
 169                 monitorProxy.stop();
 170             }
 171         },
 172         new TestCase("Remove monitored MBean within threshold listener",
 173                      When.IN_NOTIFY) {
 174             @Override
 175             void doSensitiveThing(StringMonitorMBean monitorProxy,
 176                                   ObjectName observedName) {
 177                 monitorProxy.removeObservedObject(observedName);
 178             }
 179         },
 180         new TestCase("Stop monitor within threshold listener",
 181                      When.IN_NOTIFY) {
 182             @Override
 183             void doSensitiveThing(StringMonitorMBean monitorProxy,
 184                                   ObjectName observedName) {
 185                 monitorProxy.stop();
 186             }
 187         },
 188     };
 189 
 190     public static interface TestMBean {
 191         public String getThing();
 192         public void setThing(String thing);
 193         public int getGetCount();
 194     }
 195 
 196     public static class Test implements TestMBean {
 197         public Test(Runnable runWithinGetAttribute) {
 198             this.runWithinGetAttribute = runWithinGetAttribute;
 199         }
 200 
 201         public String getThing() {
 202             Thread t = new Thread(runWithinGetAttribute);
 203             t.start();
 204             try {
 205                 t.join();
 206             } catch (InterruptedException e) {
 207                 throw new RuntimeException(e);
 208             }
 209             getCount++;
 210             return thing;
 211         }
 212 
 213         public void setThing(String thing) {
 214             this.thing = thing;
 215         }
 216 
 217         public int getGetCount() {
 218             return getCount;
 219         }
 220 
 221         private final Runnable runWithinGetAttribute;
 222         private volatile int getCount;
 223         private volatile String thing;
 224     }
 225 }