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