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  *
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 
main(String[] args)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 {
TestCase(String description, When when)64         TestCase(String description, When when) {
65             this.description = description;
66             this.when = when;
67         }
68 
run()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 
doSensitiveThing(CounterMonitorMBean monitorProxy, ObjectName observedName)141         abstract void doSensitiveThing(CounterMonitorMBean monitorProxy,
142                                        ObjectName observedName);
143 
getDescription()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 {
getThing()188         public int getThing();
setThing(int thing)189         public void setThing(int thing);
getGetCount()190         public int getGetCount();
191     }
192 
193     public static class Test implements TestMBean {
Test(Runnable runWithinGetAttribute)194         public Test(Runnable runWithinGetAttribute) {
195             this.runWithinGetAttribute = runWithinGetAttribute;
196         }
197 
getThing()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 
setThing(int thing)210         public void setThing(int thing) {
211             this.thing = thing;
212         }
213 
getGetCount()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 }
223