1 /*
2  * Copyright (c) 2001, 2021, 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 8167108 8265153
27  * @summary The test checks that ThreadInfo.getLockOwnerName() returns a
28  *   non-null string for a blocked thread and then makes repeated calls
29  *   to getThreadInfo() and ThreadInfo.getLockOwnerName() until the thread
30  *   has exited.
31  * @requires vm.jvmti
32  * @run main/othervm/native -agentlib:GetLockOwnerName GetLockOwnerName
33  */
34 
35 import java.io.PrintStream;
36 import java.lang.management.ManagementFactory;
37 import java.lang.management.ThreadInfo;
38 import java.lang.management.ThreadMXBean;
39 
40 //
41 // main               blocker           contender            releaser
42 // =================  ================  ===================  ================
43 // launch blocker
44 // <launch returns>   blocker running
45 // launch contender   enter threadLock
46 // <launch returns>   wait for notify   contender running
47 // launch releaser    :                 block on threadLock
48 // <launch returns>   :                 :                    releaser running
49 // wait for           :                 :                    wait for notify
50 //  contended enter   :                 :                    :
51 // <ready to test>    :                 :                    :
52 // getThreadInfo      :                 :                    :
53 // verify contender   :                 :                    :
54 //  lock owner name   :                 :                    :
55 //  is "blocker"      :                 :                    :
56 // notify releaser    :                 :                    :
57 // loop until thread  :                 :                    wait finishes
58 //  is NULL           :                 :                    notify blocker
59 //   get thread info  wait finishes     :                    releaser exits
60 //   get lock owner   exit threadLock   :                    :
61 //    name            blocker exits     enter threadLock     :
62 // join releaser      :                 exit threadLock      <exit finishes>
63 // <join returns>     :                 contender exits
64 // join blocker       <exit finishes>   :
65 // <join returns>                       :
66 // join contender                       <exit finishes>
67 // <join returns>
68 //
69 
70 public class GetLockOwnerName {
71     private static final String AGENT_LIB = "GetLockOwnerName";
72 
73     private static final int DEF_TIME_MAX = 60;    // default max # secs to test
74     private static final int JOIN_MAX     = 30;    // max # secs to wait for join
75 
76     public static final int TS_INIT              = 1;  // initial testState
77     public static final int TS_BLOCKER_RUNNING   = 2;  // blocker is running
78     public static final int TS_CONTENDER_RUNNING = 3;  // contender is running
79     public static final int TS_RELEASER_RUNNING  = 4;  // releaser is running
80     public static final int TS_CONTENDER_BLOCKED = 5;  // contender is blocked
81     public static final int TS_READY_TO_RELEASE  = 6;  // ready to release the blocker
82     public static final int TS_DONE_BLOCKING     = 7;  // done blocking threadLock
83     public static final int TS_CONTENDER_DONE    = 8;  // contender has run; done
84 
85     public static Object barrierLaunch = new Object();   // controls thread launch
86     public static Object barrierBlocker = new Object();  // controls blocker
87     public static Object barrierReleaser = new Object();  // controls releaser
88     public static Object threadLock = new Object();      // testing object
89 
90     public static long count = 0;
91     public static boolean printDebug = false;
92     public volatile static int testState;
93 
log(String msg)94     private static void log(String msg) { System.out.println(msg); }
95 
wait4ContendedEnter(GetLockOwnerNameWorker thr)96     native static int wait4ContendedEnter(GetLockOwnerNameWorker thr);
97 
main(String[] args)98     public static void main(String[] args) throws Exception {
99         try {
100             System.loadLibrary(AGENT_LIB);
101             log("Loaded library: " + AGENT_LIB);
102         } catch (UnsatisfiedLinkError ule) {
103             log("Failed to load library: " + AGENT_LIB);
104             log("java.library.path: " + System.getProperty("java.library.path"));
105             throw ule;
106         }
107 
108         int timeMax = 0;
109         if (args.length == 0) {
110             timeMax = DEF_TIME_MAX;
111         } else {
112             int argIndex = 0;
113             int argsLeft = args.length;
114             if (args[0].equals("-p")) {
115                 printDebug = true;
116                 argIndex = 1;
117                 argsLeft--;
118             }
119             if (argsLeft == 0) {
120                 timeMax = DEF_TIME_MAX;
121             } else if (argsLeft == 1) {
122                 try {
123                     timeMax = Integer.parseUnsignedInt(args[argIndex]);
124                 } catch (NumberFormatException nfe) {
125                     System.err.println("'" + args[argIndex] +
126                                        "': invalid timeMax value.");
127                     usage();
128                 }
129             } else {
130                 usage();
131             }
132         }
133 
134         int retCode = run(timeMax, System.out);
135         if (retCode != 0) {
136             throw new RuntimeException("Test failed with retCode=" + retCode);
137         }
138     }
139 
logDebug(String mesg)140     public static void logDebug(String mesg) {
141         if (printDebug) {
142             System.err.println(Thread.currentThread().getName() + ": " + mesg);
143         }
144     }
145 
usage()146     public static void usage() {
147         System.err.println("Usage: " + AGENT_LIB + " [-p][time_max]");
148         System.err.println("where:");
149         System.err.println("    -p        print debug info");
150         System.err.println("    time_max  max looping time in seconds");
151         System.err.println("              (default is " + DEF_TIME_MAX +
152                            " seconds)");
153         System.exit(1);
154     }
155 
run(int timeMax, PrintStream out)156     public static int run(int timeMax, PrintStream out) {
157         return (new GetLockOwnerName()).doWork(timeMax, out);
158     }
159 
checkTestState(int exp)160     public static void checkTestState(int exp) {
161         if (testState != exp) {
162             System.err.println("Failure at " + count + " loops.");
163             throw new InternalError("Unexpected test state value: "
164                 + "expected=" + exp + " actual=" + testState);
165         }
166     }
167 
doWork(int timeMax, PrintStream out)168     public int doWork(int timeMax, PrintStream out) {
169         ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
170 
171         GetLockOwnerNameWorker blocker;    // blocker thread
172         GetLockOwnerNameWorker contender;  // contender thread
173         GetLockOwnerNameWorker releaser;   // releaser thread
174 
175         System.out.println("About to execute for " + timeMax + " seconds.");
176 
177         long start_time = System.currentTimeMillis();
178         while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {
179             count++;
180             testState = TS_INIT;  // starting the test loop
181 
182             // launch the blocker thread
183             synchronized (barrierLaunch) {
184                 blocker = new GetLockOwnerNameWorker("blocker");
185                 blocker.start();
186 
187                 while (testState != TS_BLOCKER_RUNNING) {
188                     try {
189                         barrierLaunch.wait();  // wait until it is running
190                     } catch (InterruptedException ex) {
191                     }
192                 }
193             }
194 
195             // launch the contender thread
196             synchronized (barrierLaunch) {
197                 contender = new GetLockOwnerNameWorker("contender");
198                 contender.start();
199 
200                 while (testState != TS_CONTENDER_RUNNING) {
201                     try {
202                         barrierLaunch.wait();  // wait until it is running
203                     } catch (InterruptedException ex) {
204                     }
205                 }
206             }
207 
208             // launch the releaser thread
209             synchronized (barrierLaunch) {
210                 releaser = new GetLockOwnerNameWorker("releaser");
211                 releaser.start();
212 
213                 while (testState != TS_RELEASER_RUNNING) {
214                     try {
215                         barrierLaunch.wait();  // wait until it is running
216                     } catch (InterruptedException ex) {
217                     }
218                 }
219             }
220 
221             // wait for the contender thread to block
222             logDebug("before contended enter wait");
223             int retCode = wait4ContendedEnter(contender);
224             if (retCode != 0) {
225                 throw new RuntimeException("error in JVMTI GetThreadState " +
226                                            "or GetCurrentContendedMonitor " +
227                                            "retCode=" + retCode);
228             }
229             testState = TS_CONTENDER_BLOCKED;
230             logDebug("done contended enter wait");
231 
232             //
233             // At this point, all of the child threads are running
234             // and we can get to meat of the test:
235             //
236             // - query the contender thread and verify that it is blocked
237             //   by the blocker thread
238             // - tell the releaser thread to release the blocker thread
239             // - continue to query the contender thread until it exits
240             //
241             long id = 0;
242             ThreadInfo info = null;
243             int lateCount = 0;
244 
245             checkTestState(TS_CONTENDER_BLOCKED);
246 
247             id = contender.getId();
248             info = mbean.getThreadInfo(id, 0);
249             String name = info.getLockOwnerName();
250 
251             if (name == null) {
252                 out.println("Failure at " + count + " loops.");
253                 throw new RuntimeException("ThreadInfo.GetLockOwnerName() "
254                                            + "returned null name for "
255                                            + "contender.");
256             } else if (!name.equals("blocker")) {
257                 out.println("Failure at " + count + " loops.");
258                 throw new RuntimeException("name='" + name + "': name "
259                                            + "should be blocker.");
260             } else {
261                 logDebug("ThreadInfo.GetLockOwnerName() returned blocker.");
262             }
263 
264             synchronized (barrierReleaser) {
265                 // tell releaser thread to release the blocker thread
266                 testState = TS_READY_TO_RELEASE;
267                 barrierReleaser.notify();
268             }
269 
270             while (true) {
271                 // maxDepth == 0 requires no safepoint so alternate.
272                 int maxDepth = ((count % 1) == 1) ? Integer.MAX_VALUE : 0;
273                 info = mbean.getThreadInfo(id, maxDepth);
274                 if (info == null) {
275                     // the contender has exited
276                     break;
277                 }
278                 name = info.getLockOwnerName();
279                 // We can't verify that name == null here because contender
280                 // might be slow leaving the threadLock monitor.
281                 lateCount++;
282             }
283             logDebug("made " + lateCount + " late calls to getThreadInfo() " +
284                      "and info.getLockOwnerName().");
285 
286             try {
287                 releaser.join(JOIN_MAX * 1000);
288                 if (releaser.isAlive()) {
289                     System.err.println("Failure at " + count + " loops.");
290                     throw new InternalError("releaser thread is stuck");
291                 }
292                 blocker.join(JOIN_MAX * 1000);
293                 if (blocker.isAlive()) {
294                     System.err.println("Failure at " + count + " loops.");
295                     throw new InternalError("blocker thread is stuck");
296                 }
297                 contender.join(JOIN_MAX * 1000);
298                 if (contender.isAlive()) {
299                     System.err.println("Failure at " + count + " loops.");
300                     throw new InternalError("contender thread is stuck");
301                 }
302             } catch (InterruptedException ex) {
303             }
304 
305             checkTestState(TS_CONTENDER_DONE);
306         }
307 
308         System.out.println("Executed " + count + " loops in " + timeMax +
309                            " seconds.");
310 
311         return 0;
312     }
313 }
314 
315 class GetLockOwnerNameWorker extends Thread {
GetLockOwnerNameWorker(String name)316     public GetLockOwnerNameWorker(String name) {
317         super(name);
318     }
319 
run()320     public void run() {
321         GetLockOwnerName.logDebug("thread running");
322 
323         //
324         // The blocker thread:
325         // - grabs threadLock
326         // - holds threadLock until we tell it let go
327         // - releases threadLock
328         //
329         if (getName().equals("blocker")) {
330             // grab threadLock before we tell main we are running
331             GetLockOwnerName.logDebug("before enter threadLock");
332             synchronized(GetLockOwnerName.threadLock) {
333                 GetLockOwnerName.logDebug("enter threadLock");
334 
335                 GetLockOwnerName.checkTestState(GetLockOwnerName.TS_INIT);
336 
337                 synchronized(GetLockOwnerName.barrierBlocker) {
338                     synchronized(GetLockOwnerName.barrierLaunch) {
339                         // tell main we are running
340                         GetLockOwnerName.testState = GetLockOwnerName.TS_BLOCKER_RUNNING;
341                         GetLockOwnerName.barrierLaunch.notify();
342                     }
343                     GetLockOwnerName.logDebug("thread waiting");
344                     while (GetLockOwnerName.testState != GetLockOwnerName.TS_DONE_BLOCKING) {
345                         try {
346                             // wait for main to tell us when to exit threadLock
347                             GetLockOwnerName.barrierBlocker.wait();
348                         } catch (InterruptedException ex) {
349                         }
350                     }
351                 }
352                 GetLockOwnerName.logDebug("exit threadLock");
353             }
354         }
355         //
356         // The contender thread:
357         // - tries to grab the threadLock
358         // - grabs threadLock
359         // - releases threadLock
360         //
361         else if (getName().equals("contender")) {
362             synchronized(GetLockOwnerName.barrierLaunch) {
363                 // tell main we are running
364                 GetLockOwnerName.testState = GetLockOwnerName.TS_CONTENDER_RUNNING;
365                 GetLockOwnerName.barrierLaunch.notify();
366             }
367 
368             GetLockOwnerName.logDebug("before enter threadLock");
369             synchronized(GetLockOwnerName.threadLock) {
370                 GetLockOwnerName.logDebug("enter threadLock");
371 
372                 GetLockOwnerName.checkTestState(GetLockOwnerName.TS_DONE_BLOCKING);
373                 GetLockOwnerName.testState = GetLockOwnerName.TS_CONTENDER_DONE;
374 
375                 GetLockOwnerName.logDebug("exit threadLock");
376             }
377         }
378         //
379         // The releaser thread:
380         // - tries to grab the barrierBlocker (should not block!)
381         // - grabs barrierBlocker
382         // - releases the blocker thread
383         // - releases barrierBlocker
384         //
385         else if (getName().equals("releaser")) {
386             synchronized(GetLockOwnerName.barrierReleaser) {
387                 synchronized(GetLockOwnerName.barrierLaunch) {
388                     // tell main we are running
389                     GetLockOwnerName.testState = GetLockOwnerName.TS_RELEASER_RUNNING;
390                     GetLockOwnerName.barrierLaunch.notify();
391                 }
392                 GetLockOwnerName.logDebug("thread waiting");
393                 while (GetLockOwnerName.testState != GetLockOwnerName.TS_READY_TO_RELEASE) {
394                     try {
395                         // wait for main to tell us when to continue
396                         GetLockOwnerName.barrierReleaser.wait();
397                     } catch (InterruptedException ex) {
398                     }
399                 }
400             }
401 
402             GetLockOwnerName.logDebug("before enter barrierBlocker");
403             synchronized (GetLockOwnerName.barrierBlocker) {
404                 GetLockOwnerName.logDebug("enter barrierBlocker");
405 
406                 // tell blocker thread to exit threadLock
407                 GetLockOwnerName.testState = GetLockOwnerName.TS_DONE_BLOCKING;
408                 GetLockOwnerName.barrierBlocker.notify();
409 
410                 GetLockOwnerName.logDebug("released blocker thread");
411                 GetLockOwnerName.logDebug("exit barrierBlocker");
412             }
413         }
414     }
415 }
416