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