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