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