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 the synchronization statistics support:
  28  * @author  Mandy Chung
  29  * @author  Jaroslav Bachorik
  30  *
  31  * @run main/othervm SynchronizationStatistics
  32  */
  33 
  34 import java.lang.management.*;
  35 import java.util.concurrent.Phaser;
  36 import java.util.function.Supplier;
  37 
  38 public class SynchronizationStatistics {
  39     private static class LockerThread extends Thread {
  40         public LockerThread(Runnable r) {
  41             super(r, "LockerThread");
  42         }
  43     }
  44 
  45     private static final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
  46 
  47     private static final boolean blockedTimeCheck =
  48         mbean.isThreadContentionMonitoringSupported();
  49 
  50 
  51     public static void main(String args[]) throws Exception {
  52         if (blockedTimeCheck) {
  53             mbean.setThreadContentionMonitoringEnabled(true);
  54         }
  55 
  56         if (!mbean.isThreadContentionMonitoringEnabled()) {
  57             throw new RuntimeException("TEST FAILED: " +
  58                 "Thread Contention Monitoring is not enabled");
  59         }
  60 
  61         testBlockingOnSimpleMonitor();
  62         testBlockingOnNestedMonitor();
  63         testWaitingOnSimpleMonitor();
  64         testMultiWaitingOnSimpleMonitor();
  65         testWaitingOnNestedMonitor();
  66 
  67         System.out.println("Test passed.");
  68     }
  69 
  70     private static LockerThread newLockerThread(Runnable r) {
  71         LockerThread t = new LockerThread(r);
  72         t.setDaemon(true);
  73         return t;
  74     }
  75 
  76     private static void waitForThreadState(Thread t, Thread.State state) throws InterruptedException {
  77         while (t.getState() != state) {
  78             Thread.sleep(3);
  79         }
  80     }
  81 
  82     /**
  83      * Tests that blocking on a single monitor properly increases the
  84      * blocked count at least by 1. Also asserts that the correct lock name is provided.
  85      */
  86     private static void testBlockingOnSimpleMonitor() throws Exception {
  87         System.out.println("testBlockingOnSimpleMonitor");
  88         final Object lock1 = new Object();
  89         System.out.println("Lock1 = " + lock1);
  90 
  91         final Phaser p = new Phaser(2);
  92         LockerThread lt = newLockerThread(new Runnable() {
  93             @Override
  94             public void run() {
  95                 p.arriveAndAwaitAdvance(); // phase[1]
  96                 synchronized(lock1) {
  97                     System.out.println("[LockerThread obtained Lock1]");
  98                     p.arriveAndAwaitAdvance(); // phase[2]
  99                 }
 100                 p.arriveAndAwaitAdvance(); // phase[3]
 101             }
 102         });
 103 
 104         lt.start();
 105         long tid = lt.getId();
 106         ThreadInfo ti = mbean.getThreadInfo(tid);
 107         String lockName = null;
 108         synchronized(lock1) {
 109             p.arriveAndAwaitAdvance(); // phase[1]
 110             waitForThreadState(lt, Thread.State.BLOCKED);
 111             do {
 112                 lockName = mbean.getThreadInfo(tid).getLockName();
 113             } while (lockName == null);
 114         }
 115 
 116         p.arriveAndAwaitAdvance(); // phase[2]
 117         testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock1);
 118         p.arriveAndDeregister(); // phase[3]
 119 
 120         lt.join();
 121 
 122         printok();
 123     }
 124 
 125     /**
 126      * Tests that blocking on a nested monitor properly increases the
 127      * blocked count at least by 1 - it is not affected by the nesting depth.
 128      * Also asserts that the correct lock name is provided.
 129      */
 130     private static void testBlockingOnNestedMonitor() throws Exception {
 131         System.out.println("testBlockingOnNestedMonitor");
 132         final Object lock1 = new Object();
 133         final Object lock2 = new Object();
 134 
 135         System.out.println("Lock1 = " + lock1);
 136         System.out.println("Lock2 = " + lock2);
 137 
 138         final Phaser p = new Phaser(2);
 139         LockerThread lt = newLockerThread(new Runnable() {
 140             @Override
 141             public void run() {
 142                 p.arriveAndAwaitAdvance(); // phase[1]
 143                 synchronized(lock1) {
 144                     System.out.println("[LockerThread obtained Lock1]");
 145                     p.arriveAndAwaitAdvance(); // phase[2]
 146                     p.arriveAndAwaitAdvance(); // phase[3]
 147                     synchronized(lock2) {
 148                         System.out.println("[LockerThread obtained Lock2]");
 149                         p.arriveAndAwaitAdvance(); // phase[4]
 150                     }
 151                     p.arriveAndAwaitAdvance(); // phase[5]
 152                 }
 153             }
 154         });
 155 
 156         lt.start();
 157         long tid = lt.getId();
 158         ThreadInfo ti = mbean.getThreadInfo(tid);
 159         String lockName = null;
 160         synchronized(lock1) {
 161             p.arriveAndAwaitAdvance(); // phase[1]
 162             waitForThreadState(lt, Thread.State.BLOCKED);
 163             do {
 164                 lockName = mbean.getThreadInfo(tid).getLockName();
 165             } while (lockName == null);
 166         }
 167         p.arriveAndAwaitAdvance(); // phase[2]
 168 
 169         ti = testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock1);
 170 
 171         synchronized(lock2) {
 172             p.arriveAndAwaitAdvance(); // phase [3]
 173             waitForThreadState(lt, Thread.State.BLOCKED);
 174             do {
 175                 lockName = mbean.getThreadInfo(tid).getLockName();
 176             } while (lockName == null);
 177         }
 178         p.arriveAndAwaitAdvance(); // phase [4]
 179         testBlocked(ti, () -> mbean.getThreadInfo(tid), lockName, lock2);
 180         p.arriveAndDeregister();
 181 
 182         lt.join();
 183 
 184         printok();
 185     }
 186 
 187     /**
 188      * Tests that waiting on a single monitor properly increases the waited
 189      * count by 1 and the waited time by a positive number.
 190      */
 191     private static void testWaitingOnSimpleMonitor() throws Exception {
 192         System.out.println("testWaitingOnSimpleMonitor");
 193         final Object lock1 = new Object();
 194         final Phaser p = new Phaser(2);
 195         LockerThread lt = newLockerThread(new Runnable() {
 196             @Override
 197             public void run() {
 198                 p.arriveAndAwaitAdvance(); // phase[1]
 199                 synchronized(lock1) {
 200                     System.out.println("[LockerThread obtained Lock1]");
 201                     try {
 202                         lock1.wait(300);
 203                     } catch (InterruptedException ex) {
 204                         // ignore
 205                     }
 206                     p.arriveAndAwaitAdvance(); // phase[2]
 207                 }
 208                 p.arriveAndAwaitAdvance(); // phase[3]
 209             }
 210         });
 211 
 212         lt.start();
 213         ThreadInfo ti1 = mbean.getThreadInfo(lt.getId());
 214         synchronized(lock1) {
 215             p.arriveAndAwaitAdvance(); // phase[1]
 216             waitForThreadState(lt, Thread.State.BLOCKED);
 217         }
 218         p.arriveAndAwaitAdvance(); // phase[2]
 219 
 220         testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 1);
 221         p.arriveAndDeregister(); // phase[3]
 222 
 223         lt.join();
 224 
 225         printok();
 226     }
 227 
 228     /**
 229      * Tests that waiting multiple times on the same monitor subsequently
 230      * increases the waited count by the number of subsequent calls and the
 231      * waited time by a positive number.
 232      */
 233     private static void testMultiWaitingOnSimpleMonitor() throws Exception {
 234         System.out.println("testWaitingOnMultipleMonitors");
 235         final Object lock1 = new Object();
 236 
 237         final Phaser p = new Phaser(2);
 238         LockerThread lt = newLockerThread(new Runnable() {
 239             @Override
 240             public void run() {
 241                 p.arriveAndAwaitAdvance(); // phase[1]
 242                 synchronized(lock1) {
 243                     System.out.println("[LockerThread obtained Lock1]");
 244                     for (int i = 0; i < 3; i++) {
 245                         try {
 246                             lock1.wait(300);
 247                         } catch (InterruptedException ex) {
 248                             // ignore
 249                         }
 250                         p.arriveAndAwaitAdvance(); // phase[2-4]
 251                     }
 252                 }
 253                 p.arriveAndAwaitAdvance(); // phase[5]
 254             }
 255         });
 256 
 257         lt.start();
 258         ThreadInfo ti1 = mbean.getThreadInfo(lt.getId());
 259         synchronized(lock1) {
 260             p.arriveAndAwaitAdvance(); //phase[1]
 261             waitForThreadState(lt, Thread.State.BLOCKED);
 262         }
 263         int phase = p.getPhase();
 264         while ((p.arriveAndAwaitAdvance() - phase) < 3); // phase[2-4]
 265 
 266         testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 3);
 267         p.arriveAndDeregister(); // phase[5]
 268 
 269         lt.join();
 270 
 271         printok();
 272     }
 273 
 274     /**
 275      * Tests that waiting on monitors places in nested synchronized blocks
 276      * properly increases the waited count by the number of times the "lock.wait()"
 277      * was invoked and the waited time by a positive number.
 278      */
 279     private static void testWaitingOnNestedMonitor() throws Exception {
 280         System.out.println("testWaitingOnNestedMonitor");
 281         final Object lock1 = new Object();
 282         final Object lock2 = new Object();
 283         final Object lock3 = new Object();
 284 
 285         final Phaser p = new Phaser(2);
 286         LockerThread lt = newLockerThread(new Runnable() {
 287             @Override
 288             public void run() {
 289                 p.arriveAndAwaitAdvance(); // phase[1]
 290                 synchronized(lock1) {
 291                     System.out.println("[LockerThread obtained Lock1]");
 292                     try {
 293                         lock1.wait(300);
 294                     } catch (InterruptedException ex) {
 295                         // ignore
 296                     }
 297 
 298                     p.arriveAndAwaitAdvance(); // phase[2]
 299                     synchronized(lock2) {
 300                         System.out.println("[LockerThread obtained Lock2]");
 301                         try {
 302                             lock2.wait(300);
 303                         } catch (InterruptedException ex) {
 304                             // ignore
 305                         }
 306 
 307                         p.arriveAndAwaitAdvance(); // phase[3]
 308                         synchronized(lock3) {
 309                             System.out.println("[LockerThread obtained Lock3]");
 310                             try {
 311                                 lock3.wait(300);
 312                             } catch (InterruptedException ex) {
 313                                 // ignore
 314                             }
 315                             p.arriveAndAwaitAdvance(); // phase[4]
 316                         }
 317                     }
 318                 }
 319                 p.arriveAndAwaitAdvance(); // phase[5]
 320             }
 321         });
 322 
 323         lt.start();
 324         ThreadInfo ti1 = mbean.getThreadInfo(lt.getId());
 325         synchronized(lock1) {
 326             p.arriveAndAwaitAdvance(); // phase[1]
 327             waitForThreadState(lt, Thread.State.BLOCKED);
 328         }
 329 
 330         synchronized(lock2) {
 331             p.arriveAndAwaitAdvance(); // phase[2]
 332             waitForThreadState(lt, Thread.State.BLOCKED);
 333         }
 334 
 335         synchronized(lock3) {
 336             p.arriveAndAwaitAdvance(); // phase[3]
 337             waitForThreadState(lt, Thread.State.BLOCKED);
 338         }
 339 
 340         p.arriveAndAwaitAdvance(); // phase[4]
 341         testWaited(ti1, () -> mbean.getThreadInfo(lt.getId()), 3);
 342         p.arriveAndDeregister(); // phase[5]
 343 
 344         lt.join();
 345         printok();
 346     }
 347 
 348     private static void printok() {
 349         System.out.println("OK\n");
 350     }
 351 
 352     private static void testWaited(ThreadInfo ti1, Supplier<ThreadInfo> ti2, int waited)
 353     throws InterruptedException {
 354         boolean error;
 355         do {
 356             error = false;
 357             ThreadInfo ti = ti2.get();
 358             long waitCntDiff = ti.getWaitedCount() - ti1.getWaitedCount();
 359             long waitTimeDiff = ti.getWaitedTime() - ti1.getWaitedTime();
 360             if (waitCntDiff < waited) {
 361                 System.err.println(
 362                     "Unexpected diff in waited count. Expecting at least "
 363                     + waited + " , got " + waitCntDiff
 364                 );
 365                 error = true;
 366             }
 367             if (waitTimeDiff <= 0) {
 368                 System.err.println(
 369                     "Unexpected diff in waited time. Expecting increasing " +
 370                     "value, got " + waitTimeDiff + "ms"
 371                 );
 372                 error = true;
 373             }
 374             if (error) {
 375                 System.err.println("Retrying in 20ms ...");
 376                 Thread.sleep(20);
 377             }
 378         } while (error);
 379     }
 380 
 381     private static ThreadInfo testBlocked(ThreadInfo ti1, Supplier<ThreadInfo> ti2,
 382                                     String lockName, final Object lock)
 383     throws InterruptedException {
 384         boolean error;
 385         ThreadInfo ti = null;
 386         do {
 387             error = false;
 388             ti = ti2.get();
 389             long blkCntDiff = ti.getBlockedCount() - ti1.getBlockedCount();
 390             long blkTimeDiff = ti.getBlockedTime() - ti1.getBlockedTime();
 391 
 392             System.out.println("testBlocked: [" + blkCntDiff + ", " + blkTimeDiff + ", " + lockName + "]");
 393 
 394             if (blkCntDiff < 1) {
 395                 System.err.println(
 396                     "Unexpected diff in blocked count. Expecting at least 1, " +
 397                     "got " + blkCntDiff
 398                 );
 399                 error = true;
 400             }
 401             if (blkTimeDiff < 0) {
 402                 System.err.println(
 403                     "Unexpected diff in blocked time. Expecting a positive " +
 404                     "number, got " + blkTimeDiff
 405                 );
 406                 error = true;
 407             }
 408             if (!lockName.equals(lock.toString())) {
 409                 System.err.println(
 410                     "Unexpected blocked monitor name. Expecting " +
 411                     lock.toString() + ", got " + lockName
 412                 );
 413                 error = true;
 414             }
 415             if (error) {
 416                 System.err.println("Retrying in 20ms ...");
 417                 Thread.sleep(20);
 418             }
 419         } while (error);
 420         return ti;
 421     }
 422 }