1 /*
2  * Copyright (c) 2003, 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     4530538
27  * @summary Basic unit test of ThreadInfo.getBlockedCount()
28  * @author  Alexei Guibadoulline and Mandy Chung
29  * @author  Jaroslav Bachorik
30  *
31  * @run main ThreadBlockedCount
32  */
33 
34 import java.lang.management.*;
35 import java.util.concurrent.Phaser;
36 
37 public class ThreadBlockedCount {
38         static final long EXPECTED_BLOCKED_COUNT = 3;
39     static final int  DEPTH = 10;
40     private static final ThreadMXBean mbean
41         = ManagementFactory.getThreadMXBean();
42 
43     private static final Object a = new Object();
44     private static final Object b = new Object();
45     private static final Object c = new Object();
46 
47     private static final Object blockedObj1 = new Object();
48     private static final Object blockedObj2 = new Object();
49     private static final Object blockedObj3 = new Object();
50     private static volatile boolean testOk = true;
51     private static BlockingThread blocking;
52     private static BlockedThread blocked;
53 
main(String args[])54     public static void main(String args[]) throws Exception {
55         // real run
56         runTest();
57         if (!testOk) {
58             throw new RuntimeException("TEST FAILED.");
59         }
60         System.out.println("Test passed.");
61     }
62 
runTest()63     private static void runTest() throws Exception {
64         final Phaser p = new Phaser(2);
65 
66         blocking = new BlockingThread(p);
67         blocking.start();
68 
69         blocked = new BlockedThread(p);
70         blocked.start();
71 
72         try {
73             blocking.join();
74 
75             testOk = checkBlocked();
76             p.arriveAndAwaitAdvance(); // #5
77 
78         } catch (InterruptedException e) {
79             System.err.println("Unexpected exception.");
80             e.printStackTrace(System.err);
81             throw e;
82         }
83     }
84 
85 
86     static class BlockedThread extends Thread {
87         private final Phaser p;
88 
BlockedThread(Phaser p)89         BlockedThread(Phaser p) {
90             super("BlockedThread");
91             this.p = p;
92         }
93 
run()94         public void run() {
95             int accumulator = 0;
96             p.arriveAndAwaitAdvance(); // #1
97 
98             // Enter lock a without blocking
99             synchronized (a) {
100                 p.arriveAndAwaitAdvance(); // #2
101 
102                 // Block to enter blockedObj1
103                 // blockedObj1 should be owned by BlockingThread
104                 synchronized (blockedObj1) {
105                     accumulator++; // filler
106                 }
107             }
108 
109             // Enter lock a without blocking
110             synchronized (b) {
111                 // wait until BlockingThread holds blockedObj2
112                 p.arriveAndAwaitAdvance(); // #3
113 
114                 // Block to enter blockedObj2
115                 // blockedObj2 should be owned by BlockingThread
116                 synchronized (blockedObj2) {
117                     accumulator++; // filler
118                 }
119             }
120 
121             // Enter lock a without blocking
122             synchronized (c) {
123                 // wait until BlockingThread holds blockedObj3
124                 p.arriveAndAwaitAdvance(); // #4
125 
126                 // Block to enter blockedObj3
127                 // blockedObj3 should be owned by BlockingThread
128                 synchronized (blockedObj3) {
129                     accumulator++; // filler
130                 }
131             }
132 
133             // wait for the main thread to check the blocked count
134             System.out.println("Acquired " + accumulator + " monitors");
135             p.arriveAndAwaitAdvance(); // #5
136             // ... and we can leave now
137         } // run()
138     } // BlockedThread
139 
140     static class BlockingThread extends Thread {
141         private final Phaser p;
142 
BlockingThread(Phaser p)143         BlockingThread(Phaser p) {
144             super("BlockingThread");
145             this.p = p;
146         }
147 
waitForBlocked()148         private void waitForBlocked() {
149             // wait for BlockedThread.
150             p.arriveAndAwaitAdvance();
151 
152             boolean threadBlocked = false;
153             while (!threadBlocked) {
154                 // give a chance for BlockedThread to really block
155                 try {
156                     Thread.sleep(50);
157                 } catch (InterruptedException e) {
158                     System.err.println("Unexpected exception.");
159                     e.printStackTrace(System.err);
160                     testOk = false;
161                     break;
162                 }
163                 ThreadInfo info = mbean.getThreadInfo(blocked.getId());
164                 threadBlocked = (info.getThreadState() == Thread.State.BLOCKED);
165             }
166         }
167 
run()168         public void run() {
169             p.arriveAndAwaitAdvance(); // #1
170 
171             synchronized (blockedObj1) {
172                 System.out.println("BlockingThread attempts to notify a");
173                 waitForBlocked(); // #2
174             }
175 
176             // block until BlockedThread is ready
177             synchronized (blockedObj2) {
178                 System.out.println("BlockingThread attempts to notify b");
179                 waitForBlocked(); // #3
180             }
181 
182             // block until BlockedThread is ready
183             synchronized (blockedObj3) {
184                 System.out.println("BlockingThread attempts to notify c");
185                 waitForBlocked(); // #4
186             }
187 
188         } // run()
189     } // BlockingThread
190 
getBlockedCount()191     private static long getBlockedCount() {
192         long count;
193         // Check the mbean now
194         ThreadInfo ti = mbean.getThreadInfo(blocked.getId());
195         count = ti.getBlockedCount();
196         return count;
197     }
198 
checkBlocked()199     private static boolean checkBlocked() {
200         // wait for the thread stats to be updated for 10 seconds
201         long count = -1;
202         for (int i = 0; i < 100; i++) {
203             count = getBlockedCount();
204             if (count >= EXPECTED_BLOCKED_COUNT) {
205                 return true;
206             }
207             try {
208                 Thread.sleep(100);
209             } catch (InterruptedException e) {
210                 System.err.println("Unexpected exception.");
211                 e.printStackTrace(System.err);
212                 return false;
213             }
214         }
215         System.err.println("TEST FAILED: Blocked thread has " + count +
216                             " blocked counts. Expected at least " +
217                             EXPECTED_BLOCKED_COUNT);
218         return false;
219     }
220 }
221