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 
main(String[] args)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 {
TestCase(String description, When when)65         TestCase(String description, When when) {
66             this.description = description;
67             this.when = when;
68         }
69 
run()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 
doSensitiveThing(StringMonitorMBean monitorProxy, ObjectName observedName)144         abstract void doSensitiveThing(StringMonitorMBean monitorProxy,
145                                        ObjectName observedName);
146 
getDescription()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 {
getThing()191         public String getThing();
setThing(String thing)192         public void setThing(String thing);
getGetCount()193         public int getGetCount();
194     }
195 
196     public static class Test implements TestMBean {
Test(Runnable runWithinGetAttribute)197         public Test(Runnable runWithinGetAttribute) {
198             this.runWithinGetAttribute = runWithinGetAttribute;
199         }
200 
getThing()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 
setThing(String thing)213         public void setThing(String thing) {
214             this.thing = thing;
215         }
216 
getGetCount()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 }
226