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 * @key intermittent 28 * @summary Test that no locks are held when a monitor attribute is sampled 29 * or notif delivered. 30 * @author Eamonn McManus 31 * 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 }