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 }