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