1 /* 2 * Copyright (c) 2003, 2016, 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.getLockName() 28 * and ThreadInfo.getLockOwnerName() 29 * @author Mandy Chung 30 * @author Jaroslav Bachorik 31 * 32 * @library /lib/testlibrary /test/lib 33 * 34 * @build jdk.testlibrary.* 35 * @run main/othervm Locks 36 */ 37 import java.lang.management.*; 38 import java.util.Arrays; 39 import java.util.Objects; 40 import java.util.Optional; 41 import java.util.concurrent.Phaser; 42 import java.util.function.Predicate; 43 import jdk.test.lib.log.LockFreeLogManager; 44 45 public class Locks { 46 47 private static final Object OBJA = new Object(); 48 private static final Object OBJB = new Object(); 49 private static final EnhancedWaiter OBJC = new EnhancedWaiter(); 50 private static final ThreadMXBean TM = ManagementFactory.getThreadMXBean(); 51 private static final LockFreeLogManager LOGGER = new LockFreeLogManager(); 52 53 private static String getLockName(Object lock) { 54 if (lock == null) return null; 55 56 return lock.getClass().getName() + '@' + 57 Integer.toHexString(System.identityHashCode(lock)); 58 } 59 60 private static void assertNoLock(Thread t) { 61 if (t == null) { 62 return; 63 } 64 String name = t.getName(); 65 Optional<ThreadInfo> result = Arrays.stream( 66 TM.getThreadInfo(TM.getAllThreadIds(), true, true)) 67 .filter(Objects::nonNull) 68 .filter(i -> name.equals(i.getLockOwnerName())) 69 .findAny(); 70 if (result.isPresent()) { 71 throw new RuntimeException("Thread " + t.getName() + " is not " 72 + "supposed to be hold any lock. Currently owning lock : " 73 + result.get().getLockName()); 74 } 75 } 76 77 /* 78 * Handy debug function to check if error condition is because of test code or not. 79 */ 80 private static void printStackTrace(Thread thread) { 81 if (thread == null) { 82 return; 83 } 84 StackTraceElement[] stackTrace = thread.getStackTrace(); 85 log("Stack dump : Thread -> " + thread.getName()); 86 for (StackTraceElement stackTraceEl : stackTrace) { 87 log("\t" + stackTraceEl.toString()); 88 } 89 } 90 91 private static void assertThreadState(Thread t, Thread.State expectedState) { 92 long tid = t.getId(); 93 if (expectedState == Thread.State.BLOCKED 94 && TM.getThreadInfo(tid).getThreadState() != Thread.State.BLOCKED) { 95 int retryCount = 0; 96 printStackTrace(t); 97 while (TM.getThreadInfo(tid).getThreadState() != Thread.State.BLOCKED) { 98 if (retryCount++ > 500) { 99 printStackTrace(t); 100 throw new RuntimeException("Thread " + t.getName() + " is at " 101 + TM.getThreadInfo(tid).getThreadState() + " state but is expected to " 102 + "be in Thread.State = " + expectedState); 103 } 104 goSleep(100); 105 } 106 } 107 if (!TM.getThreadInfo(tid).getThreadState().equals(expectedState)) { 108 printStackTrace(t); 109 throw new RuntimeException("Thread " + t.getName() + " is at " 110 + TM.getThreadInfo(tid).getThreadState() + " state but is expected to " 111 + "be in Thread.State = " + expectedState); 112 } 113 } 114 115 /* 116 * Do slow check if thread is blocked on a lock. It is possible that last thread 117 * to come out of Phaser might still be in Phaser call stack (Unsafe.park) and 118 * hence might eventually acquire expected lock. 119 */ 120 private static void checkBlockedObject(Thread t, Object lock, Thread owner) { 121 long tid = t.getId(); 122 String result = TM.getThreadInfo(tid).getLockName(); 123 final String expectedLock = (lock != null ? getLockName(lock) : null); 124 Predicate<String> p = (res) -> ((res != null && !res.equals(expectedLock)) 125 || (res == null && expectedLock != null)); 126 127 if (p.test(result)) { 128 printStackTrace(t); 129 int retryCount = 0; 130 while (p.test(result)) { 131 if (retryCount++ > 500) { 132 printStackTrace(t); 133 throw new RuntimeException("Thread " + t.getName() + " is blocked on " 134 + expectedLock + " but got " + result); 135 } 136 goSleep(100); 137 result = TM.getThreadInfo(tid).getLockName(); 138 } 139 } 140 141 result = TM.getThreadInfo(tid).getLockOwnerName(); 142 final String expectedOwner = (owner != null ? owner.getName() : null); 143 144 p = (res) -> ((res != null && !res.equals(expectedOwner)) 145 || (res == null && expectedOwner != null)); 146 if (p.test(result)) { 147 printStackTrace(t); 148 throw new RuntimeException("Owner of " + lock + " should be " 149 + expectedOwner + " but got " + result); 150 } 151 } 152 153 private static void goSleep(long ms){ 154 try { 155 Thread.sleep(ms); 156 } catch (InterruptedException ex) { 157 throw new RuntimeException(ex); 158 } 159 } 160 161 private static volatile int dummyCounter = 0; 162 163 static class LockAThread extends Thread { 164 private final Phaser p; 165 public LockAThread(Phaser p) { 166 super("LockAThread"); 167 this.p = p; 168 } 169 @Override 170 public void run() { 171 synchronized(OBJA) { 172 // block here while LockBThread holds OBJB 173 log("LockAThread about to block on OBJB"); 174 p.arriveAndAwaitAdvance(); // Phase 1 (blocking) 175 synchronized(OBJB) { 176 dummyCounter++; 177 } 178 } 179 p.arriveAndAwaitAdvance(); // Phase 2 (blocking) 180 log("LockAThread about to exit"); 181 // Make sure the current thread is not holding any lock 182 assertNoLock(this); 183 } 184 } 185 186 static class LockBThread extends Thread { 187 private final Phaser p; 188 public LockBThread(Phaser p) { 189 super("LockBThread"); 190 this.p = p; 191 } 192 @Override 193 public void run() { 194 synchronized(OBJB) { 195 log("LockBThread about to block on OBJC"); 196 p.arriveAndAwaitAdvance(); // Phase 1 (blocking) 197 // Signal main thread about to block on OBJC 198 synchronized(OBJC) { 199 dummyCounter++; 200 } 201 } 202 p.arriveAndAwaitAdvance(); // Phase 2 (blocking) 203 log("LockBThread about to exit"); 204 // Make sure the current thread is not holding any lock 205 assertNoLock(this); 206 } 207 } 208 209 /* 210 * Must be invoked from within a synchronized context 211 */ 212 private static class EnhancedWaiter { 213 214 boolean isNotified = false; 215 216 public void doWait() throws InterruptedException { 217 while (!isNotified) { 218 wait(); 219 } 220 isNotified = false; 221 } 222 223 public void doNotify() { 224 isNotified = true; 225 notify(); 226 } 227 } 228 229 private static WaitingThread waiter; 230 private static final Object ready = new Object(); 231 private static CheckerThread checker; 232 233 static class WaitingThread extends Thread { 234 private final Phaser p; 235 236 volatile boolean waiting = false; 237 238 public WaitingThread(Phaser p) { 239 super("WaitingThread"); 240 this.p = p; 241 } 242 @Override 243 public void run() { 244 synchronized(OBJC) { 245 log("WaitingThread about to wait on OBJC"); 246 try { 247 // Signal checker thread, about to wait on OBJC. 248 waiting = false; 249 p.arriveAndAwaitAdvance(); // Phase 1 (waiting) 250 waiting = true; 251 OBJC.doWait(); 252 } catch (InterruptedException e) { 253 throw new RuntimeException(e); // Do not continue test 254 } 255 256 // block until CheckerThread finishes checking 257 log("WaitingThread about to block on ready"); 258 // signal checker thread that it is about acquire 259 // object ready. 260 p.arriveAndAwaitAdvance(); // Phase 2 (waiting) 261 synchronized(ready) { 262 dummyCounter++; 263 } 264 } 265 synchronized(OBJC) { 266 try { 267 // signal checker thread, about to wait on OBJC 268 waiting = false; 269 p.arriveAndAwaitAdvance(); // Phase 3 (waiting) 270 waiting = true; 271 OBJC.doWait(); 272 } catch (InterruptedException e) { 273 throw new RuntimeException(e); 274 } 275 } 276 log("WaitingThread about to exit waiting on OBJC 2"); 277 } 278 279 public void waitForWaiting() { 280 p.arriveAndAwaitAdvance(); 281 while (!waiting) { 282 goSleep(10); 283 } 284 waitForState(State.WAITING); 285 } 286 287 public void waitForBlocked() { 288 p.arriveAndAwaitAdvance(); 289 waitForState(State.BLOCKED); 290 } 291 292 private void waitForState(Thread.State state) { 293 while (!waiter.isInterrupted() && waiter.getState() != state) { 294 Thread.yield(); 295 } 296 } 297 } 298 static class CheckerThread extends Thread { 299 public CheckerThread() { 300 super("CheckerThread"); 301 } 302 303 @Override 304 public void run() { 305 synchronized(ready) { 306 // wait until WaitingThread about to wait for OBJC 307 waiter.waitForWaiting(); // Phase 1 (waiting) 308 assertThreadState(waiter, Thread.State.WAITING); 309 checkBlockedObject(waiter, OBJC, null); 310 311 synchronized(OBJC) { 312 OBJC.doNotify(); 313 } 314 // wait for waiter thread to about to enter 315 // synchronized object ready. 316 waiter.waitForBlocked(); // Phase 2 (waiting) 317 assertThreadState(waiter, Thread.State.BLOCKED); 318 checkBlockedObject(waiter, ready, this); 319 } 320 321 // wait for signal from waiting thread that it is about 322 // wait for OBJC. 323 waiter.waitForWaiting(); // Phase 3 (waiting) 324 synchronized(OBJC) { 325 assertThreadState(waiter, Thread.State.WAITING); 326 checkBlockedObject(waiter, OBJC, Thread.currentThread()); 327 OBJC.doNotify(); 328 } 329 } 330 } 331 332 public static void main(String args[]) throws Exception { 333 try { 334 Thread mainThread = Thread.currentThread(); 335 336 // Test uncontested case 337 LockAThread t1; 338 LockBThread t2; 339 340 Phaser p = new Phaser(3); 341 synchronized(OBJC) { 342 // Make sure the main thread is not holding any lock 343 assertNoLock(mainThread); 344 345 // Test deadlock case 346 // t1 holds lockA and attempts to lock B 347 // t2 holds lockB and attempts to lock C 348 t1 = new LockAThread(p); 349 t1.start(); 350 351 t2 = new LockBThread(p); 352 t2.start(); 353 354 p.arriveAndAwaitAdvance(); // Phase 1 (blocking) 355 assertThreadState(t2, Thread.State.BLOCKED); 356 checkBlockedObject(t2, OBJC, mainThread); 357 assertThreadState(t1, Thread.State.BLOCKED); 358 checkBlockedObject(t1, OBJB, t2); 359 360 long[] expectedThreads = new long[3]; 361 expectedThreads[0] = t1.getId(); // blocked on lockB 362 expectedThreads[1] = t2.getId(); // owner of lockB blocking on lockC 363 expectedThreads[2] = mainThread.getId(); // owner of lockC 364 findThreadsBlockedOn(OBJB, expectedThreads); 365 } 366 p.arriveAndAwaitAdvance(); // Phase 2 (blocking) 367 368 p = new Phaser(2); 369 // Test Object.wait() case 370 waiter = new WaitingThread(p); 371 waiter.start(); 372 373 checker = new CheckerThread(); 374 checker.start(); 375 376 try { 377 waiter.join(); 378 checker.join(); 379 } catch (InterruptedException e) { 380 throw new RuntimeException(e); 381 } 382 } finally { // log all the messages to STDOUT 383 System.out.println(LOGGER.toString()); 384 } 385 System.out.println("Test passed."); 386 } 387 388 private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock) 389 throws Exception { 390 ThreadInfo ownerInfo = null; 391 for (ThreadInfo info : infos) { 392 String blockedLock = info.getLockName(); 393 if (lock.equals(blockedLock)) { 394 long threadId = info.getLockOwnerId(); 395 if (threadId == -1) { 396 throw new RuntimeException("TEST FAILED: " + 397 lock + " expected to have owner"); 398 } 399 for (ThreadInfo info1 : infos) { 400 if (info1.getThreadId() == threadId) { 401 ownerInfo = info1; 402 break; 403 } 404 } 405 } 406 } 407 return ownerInfo; 408 } 409 private static void findThreadsBlockedOn(Object o, long[] expectedThreads) 410 throws Exception { 411 String lock = getLockName(o); 412 // Check with ThreadInfo with no stack trace (i.e. no safepoint) 413 ThreadInfo[] infos = TM.getThreadInfo(TM.getAllThreadIds()); 414 doCheck(infos, lock, expectedThreads); 415 416 // Check with ThreadInfo with stack trace 417 infos = TM.getThreadInfo(TM.getAllThreadIds(), 1); 418 doCheck(infos, lock, expectedThreads); 419 } 420 421 private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThreads) 422 throws Exception { 423 ThreadInfo ownerInfo = null; 424 // Find the thread who is blocking on lock 425 for (ThreadInfo info : infos) { 426 String blockedLock = info.getLockName(); 427 if (lock.equals(blockedLock)) { 428 log("%s blocked on %s", info.getThreadName(), blockedLock); 429 ownerInfo = info; 430 } 431 } 432 if (ownerInfo == null) { 433 throw new RuntimeException("TEST FAILED: " + 434 "Can't retrieve ThreadInfo for the blocked thread"); 435 } 436 437 long[] threads = new long[10]; 438 int count = 0; 439 threads[count++] = ownerInfo.getThreadId(); 440 while (ownerInfo.getThreadState() == Thread.State.BLOCKED) { 441 ownerInfo = findOwnerInfo(infos, lock); 442 threads[count++] = ownerInfo.getThreadId(); 443 log(" Owner = %s id = %d", 444 ownerInfo.getThreadName(), 445 ownerInfo.getThreadId() 446 ); 447 lock = ownerInfo.getLockName(); 448 log("%s Id = %d blocked on %s", 449 ownerInfo.getThreadName(), 450 ownerInfo.getThreadId(), 451 lock 452 ); 453 } 454 log(""); 455 456 if (count != expectedThreads.length) { 457 throw new RuntimeException("TEST FAILED: " + 458 "Expected chain of threads not matched; current count =" + count); 459 } 460 for (int i = 0; i < count; i++) { 461 if (threads[i] != expectedThreads[i]) { 462 log("TEST FAILED: Unexpected thread in the chain %s expected to be %s", 463 threads[i], 464 expectedThreads[i] 465 ); 466 } 467 } 468 } 469 470 private static void log(String format, Object ... args) { 471 LOGGER.log(format + "%n", args); 472 } 473 }