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