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