1 /* 2 * Copyright (c) 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 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 StringMonitorDeadlockTest 31 * @run build StringMonitorDeadlockTest 32 * @run main StringMonitorDeadlockTest 1 33 * @run main StringMonitorDeadlockTest 2 34 * @run main StringMonitorDeadlockTest 3 35 * @run main StringMonitorDeadlockTest 4 36 */ 37 38 import java.lang.management.ManagementFactory; 39 import java.util.concurrent.atomic.AtomicInteger; 40 import javax.management.Attribute; 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.StringMonitor; 47 import javax.management.monitor.StringMonitorMBean; 48 49 public class StringMonitorDeadlockTest { 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 StringMonitor(), monitorName); 74 final StringMonitorMBean monitorProxy = 75 JMX.newMBeanProxy(mbs, monitorName, StringMonitorMBean.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.setStringToCompare("old"); 96 monitorProxy.setGranularityPeriod(10L); // 10 ms 97 monitorProxy.setNotifyDiffer(true); 98 monitorProxy.start(); 99 100 final int initGetCount = observedProxy.getGetCount(); 101 int getCount = initGetCount; 102 for (int i = 0; i < 500; i++) { // 500 * 10 = 5 seconds 103 getCount = observedProxy.getGetCount(); 104 if (getCount != initGetCount) 105 break; 106 Thread.sleep(10); 107 } 108 if (getCount <= initGetCount) 109 throw new Exception("Test failed: presumable deadlock"); 110 // This won't show up as a deadlock in CTRL-\ or in 111 // ThreadMXBean.findDeadlockedThreads(), because they don't 112 // see that thread A is waiting for thread B (B.join()), and 113 // thread B is waiting for a lock held by thread A 114 115 // Now we know the monitor has observed the initial value, 116 // so if we want to test notify behaviour we can trigger by 117 // exceeding the threshold. 118 if (when == When.IN_NOTIFY) { 119 final AtomicInteger notifCount = new AtomicInteger(); 120 final NotificationListener listener = new NotificationListener() { 121 public void handleNotification(Notification n, Object h) { 122 Thread t = new Thread(sensitiveThing); 123 t.start(); 124 try { 125 t.join(); 126 } catch (InterruptedException e) { 127 throw new RuntimeException(e); 128 } 129 notifCount.incrementAndGet(); 130 } 131 }; 132 mbs.addNotificationListener(monitorName, listener, null, null); 133 observedProxy.setThing("new"); 134 for (int i = 0; i < 500 && notifCount.get() == 0; i++) 135 Thread.sleep(10); 136 if (notifCount.get() == 0) 137 throw new Exception("Test failed: presumable deadlock"); 138 } 139 140 } 141 142 abstract void doSensitiveThing(StringMonitorMBean monitorProxy, 143 ObjectName observedName); 144 145 String getDescription() { 146 return description; 147 } 148 149 private final String description; 150 private final When when; 151 } 152 153 private static final TestCase[] testCases = { 154 new TestCase("Remove monitored MBean within monitored getAttribute", 155 When.IN_GET_ATTRIBUTE) { 156 @Override 157 void doSensitiveThing(StringMonitorMBean monitorProxy, 158 ObjectName observedName) { 159 monitorProxy.removeObservedObject(observedName); 160 } 161 }, 162 new TestCase("Stop monitor within monitored getAttribute", 163 When.IN_GET_ATTRIBUTE) { 164 @Override 165 void doSensitiveThing(StringMonitorMBean monitorProxy, 166 ObjectName observedName) { 167 monitorProxy.stop(); 168 } 169 }, 170 new TestCase("Remove monitored MBean within threshold listener", 171 When.IN_NOTIFY) { 172 @Override 173 void doSensitiveThing(StringMonitorMBean monitorProxy, 174 ObjectName observedName) { 175 monitorProxy.removeObservedObject(observedName); 176 } 177 }, 178 new TestCase("Stop monitor within threshold listener", 179 When.IN_NOTIFY) { 180 @Override 181 void doSensitiveThing(StringMonitorMBean monitorProxy, 182 ObjectName observedName) { 183 monitorProxy.stop(); 184 } 185 }, 186 }; 187 188 public static interface TestMBean { 189 public String getThing(); 190 public void setThing(String thing); 191 public int getGetCount(); 192 } 193 194 public static class Test implements TestMBean { 195 public Test(Runnable runWithinGetAttribute) { 196 this.runWithinGetAttribute = runWithinGetAttribute; 197 } 198 199 public String getThing() { 200 Thread t = new Thread(runWithinGetAttribute); 201 t.start(); 202 try { 203 t.join(); 204 } catch (InterruptedException e) { 205 throw new RuntimeException(e); 206 } 207 getCount++; 208 return thing; 209 } 210 211 public void setThing(String thing) { 212 this.thing = thing; 213 } 214 215 public int getGetCount() { 216 return getCount; 217 } 218 219 private final Runnable runWithinGetAttribute; 220 private volatile int getCount; 221 private volatile String thing; 222 } 223 }